Fetching documentation…
Registers a side effect that runs after component render and re-runs when dependencies change. Analogous to React's useEffect but with Morph's reactive model.
function morphEffect(effect: () => void | (() => void), deps?: readonly any[]): void| Parameter | Type | Description |
|---|---|---|
effect |
() => void |
Effect body function. Can return a cleanup function. |
deps |
readonly any[] |
Optional dependency array. [] runs once after the first render. With deps, re-runs when a listed dependency changes. When omitted, subscribes to signals read in the body. |
void
import { morphEffect } from 'morph'
export default function Component() {
morphEffect(() => {
// Empty deps = runs once after first render
console.log("Component mounted")
}, [])
return <div>Hello</div>
}import { morphEffect, morphState } from 'morph'
export default function Counter() {
const [count, setCount] = morphState(0)
morphEffect(() => {
// Runs after render, and re-runs when count changes
console.log("Count changed:", count)
}, [count])
return (
<div>
<text>Count: {count}</text>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
)
}If the effect returns a function, it runs on cleanup (before re-run or unmount):
import { morphEffect, morphState } from 'morph'
export default function Timer() {
const [seconds, setSeconds] = morphState(0)
morphEffect(() => {
// Reading `seconds` subscribes the effect: every tick schedules the
// next timeout, and the previous one is cleared first.
const id = setTimeout(() => setSeconds(seconds + 1), 1000)
// Cleanup: runs before re-run or unmount
return () => clearTimeout(id)
}, [seconds])
return <text>{seconds}s</text>
}| Rule | Enforcement |
|---|---|
| Must be called inside a component function | convention (no scope lint in v1) |
| Must be called on every render in same order | convention (positional matching) |
| First argument must be a function | mx-effect-cb error |
| Deps should be state values | mx-effect-deps warning |
morphEffect(fn, deps?) creates a morph::create_effect in the C++ runtime.deps is [], the effect runs once after the first render and never re-runs. Run-once effects are fire-and-forget: a returned cleanup is not stored, so timers started there live for the app's lifetime.deps lists state getters, the effect re-runs only when a listed dependency changes — other signals read in the body do not re-trigger it. If deps is omitted, the effect subscribes to whatever signals the body reads.const [theme, setTheme] = morphShared<'light' | 'dark'>('light')
morphEffect(() => {
console.log("Theme changed:", theme)
}, [theme])morphEffect(() => {
if (userId) {
loadUserProfile(userId)
}
}, [userId]) // only re-runs when userId changes