React useEffect - discussion (2022-07-30)
We cleaned up a bunch of effects recently and it made me wonder: what effect conventions actually scale, and which ones are always a regret later?
What are your hard rules for effects (deps discipline, cleanup discipline, naming/signals)? Which effect use cases do you still like (subscriptions, DOM, timers) vs avoid (syncing app state)? Do you render any effect signals so timing bugs are explainable without console archaeology?
Comments (10)
Back to latestThe effects I regret are always the ones that "fix" state after render. Subscriptions are fine.
Our main rule: if an effect writes app state, it must render a signal somewhere.
If it's invisible, it will become a "sometimes" bug you can't reproduce.
Related: Core Concepts Best Practices and the "audit trail" framing in Performance Audit Trails (Deep Dive #6).
We standardized effect naming + cleanup signals and it helped a lot with subscriptions:
tsx
function useNamedSubscription(name: string, subscribe: () => () => void) {
useEffect(() => {
setEffectSignal({ name, status: 'start', at: Date.now() });
const unsub = subscribe();
return () => {
setEffectSignal({ name, status: 'cleanup', at: Date.now() });
unsub();
};
}, [name, subscribe]);
}Counterpoint: signals for every effect can become noise fast.
We only render signals for effects that are known footguns: subscriptions, timers, and anything tied to navigation.
That seems reasonable. Our worst bugs were always subscription cleanup or URL-sync effects.
We can probably scope signals to the high-risk ones.
Yep. If it affects route posture or external systems, make it visible.
Strict Mode made our "sync effects" explode.
It was a gift. Everything that depended on single-run timing had to be rewritten.
We still use effects for analytics, but we tie them to route intent keys so they don't double fire on remounts.
If we can't make it idempotent, it doesn't ship.
I like a simple rule: effects can touch the outside world, not the inside world. If it's inside-world sync, derive it.
If the UI contract needs something, write it as a key. Don't use an effect to "eventually" make it true.
Cleanup is where bugs hide. If cleanup isn't boring, the effect is doing too much.
Our best review question: can you explain this effect from a screenshot? If not, add a signal or delete the effect.