react-runtime-hooks-z is a small, focused library of runtime-level React hooks for managing effects, lifecycles, scheduling, and reactive side-effects — without abusing useEffect.
React renders. Effects live in a runtime.
-
Precise effect lifecycles (mount / update / unmount)
-
Abortable & async-safe effects
-
Fine-grained dependency tracking (no deps array hell)
-
Zero unnecessary re-renders
-
Effect logic that is explicit, testable, and composable
-
Effect instance — created per dependency change
-
EffectContext — lifetime of one effect instance
-
watch() — reactive source instead of deps array
-
compare() — custom equality for updates
-
Scheduler — control when effects run
-
Transaction — batch effect updates
-
Dependency change = old effect aborted, new effect created.
npm install react-runtime-hooks-zuseEffectFlow
useEffectFlow(flow => {
flow.onMount(() => connect())
flow.onUpdate(({ prev, next }) => {
if (prev.id !== next.id) {
fetchData(next.id)
}
})
flow.onUnmount(() => cleanup())
}, () => user)useEffectFlow(flow => {
flow.onUpdate(async ({ next, effect }) => {
const res = await fetch(`/api/user/${next.id}`, {
signal: effect.signal
})
const data = await res.json()
console.log(data)
})
}, () => user)-
Dependency changes automatically abort the previous request
-
No stale updates, no race conditions
useEffectFlow(flow => {
flow.onUpdate(({ next }) => {
const [userId, token, locale] = next
loadUser(userId, token, locale)
})
},
() => [user.id, token, locale],
shallowArrayEqual
)useEffectFlow(flow => {
flow.onUpdate(({ effect }) => {
const id = setInterval(tick, 1000)
effect.onCleanup(() => {
clearInterval(id)
})
})
}, () => enabled)- Cleanup is tied to the effect instance, not the component.
useLifecycleEffect(
{
mount() {
console.log("mounted")
},
update({ prev, next }) {
console.log("changed", prev, next)
},
unmount() {
console.log("unmounted")
}
},
() => value
)useReactiveEffect(
() => user.id,
(id) => {
console.log("User id changed:", id)
}
)usePhaseEffect("mount", () => {
console.log("mounted")
})Supported phases: mount | update | unmount
useVisibleEffect(() => {
console.log("element is visible")
})Runs when the component enters the viewport.
useIdleEffect(() => {
preloadHeavyStuff()
})Runs during browser idle time.
import { transaction } from "react-runtime-hooks-z"
transaction(() => {
updateA()
updateB()
})Ensures effects run only once after the batch.
| Feature | react-runtime-hooks-z | useEffect |
|---|---|---|
| Abortable async | ✅ | ❌ |
| Instance-based cleanup | ✅ | |
| Custom dependency compare | ✅ | ❌ |
| Multi-source watch | ✅ | ❌ |
| Scheduler support | ✅ | ❌ |
| Accidental re-renders | ❌ |
-
Using useEffect for complex async flows
-
Encoding orchestration logic into deps arrays
-
Letting stale effects mutate newer state
-
Tying side-effects directly to render cycles
-
Effects are runtime constructs, not render side-effects
-
Dependencies should be explicit
-
Cleanup should be automatic and scoped
-
React should focus on rendering, not orchestration
MIT