You Might Not Need an Effect - discussion (2023-02-14)
The "You Might Not Need an Effect" page keeps showing up in code reviews for a reason: so many effects are just derivations or event handlers wearing an effect costume. In the React Tips vocabulary, the alternative is usually: make the contract explicit—derived outputs are derived outputs, orchestration is a named flow, and both leave evidence (logs + UI) so behavior isn't invisible.
Which effect patterns did you remove after internalizing this, and what did you replace them with? How do you decide between derive-in-render, stored-derived, and a named orchestration flow effect? Do you log effect/flow behavior as readable contract lines so it's debuggable (especially for async timing)?
Comments (16)
Back to latestBiggest removal: effects that sync props into state.
We either derive in render or commit into a single source of truth on explicit boundaries. "Sync" effects are usually trying to patch a model problem.
We started writing contract logs that distinguish derive vs flow explicitly:
txt
[tips] derive=summary reason=inputsChanged keys=3
[tips] flow=nav:normalize reason=query:missingDefaults action=replaceState
That separation prevented the classic mistake: doing orchestration work under the guise of "derivation".
Counterpoint: the page can be read as "avoid effects" and teams then do worse things (work in render, random timeouts).
I like a different framing: keep effects, but make them named, bounded, and observable.
Yes. The win isn't banning effects; it's banning mystery effects.
If an effect exists, it should have a reason vocabulary and should produce evidence.
We replaced a lot of effects with stored-derived outputs because multiple panels needed the same computed result.
Once derived outputs were centralized, the UI stopped drifting between panels.
Long-form: effects become a problem when they hide intent boundaries.
If the user clicked a button, that handler is the boundary. If the route changed, that's the boundary. If you move work into an effect "because it's after render", you lose the ability to explain why work happened.
Heuristic that worked: if the effect writes state based on state, it's probably derived.
If the effect coordinates the outside world (history, network, focus), it's orchestration and should be named and logged as such.
Counterpoint: derive-in-render can be expensive and hurt INP.
We moved heavy derives into stored-derived and kept light derives in render, then logged compute/derive separately so perf work was grounded.
We also made a dev-only effect log buffer and it revealed redundancy.
Once you can see effect spam, you can fix it by moving work to boundaries or by centralizing derive.
Long-form counterpoint: teams often replace effects with custom hooks that do the same hidden work.
The fix isn't "no effects"—it's making the work observable and tying it to a contract: lane transitions, intent strings, evidence keys.
Docs tie-in: this page pairs well with patterns and API reference guidance that normalizes named effects + evidence strings.
Related: Patterns Library and API Reference.
We use logs to ensure flows are narratable:
txt
[tips] intent=filter:commit reason=user:enter
[tips] derive=results reason=filter:commit count=27
If you can't narrate it, users can't trust it.
Long-form: the page is essentially a call to make dependencies and contracts explicit.
Even when you keep an effect, the exercise of justifying it often improves the system because it forces clarity about boundaries.
We also stopped doing navigation in effects unless it was a named normalization flow.
Silent replaceState calls are a great way to make users feel like the app is haunted.
Counterpoint: effects are sometimes the cleanest place to integrate non-React APIs (subscriptions).
But even then, I like having the effect emit a readable artifact (log line, evidence key) so it's part of the system story.
We treat drafts as multi-truth and commit on boundaries. That removed a lot of effects that existed just to keep drafts and committed truth "in sync".
Once you accept multi-truth, sync effects become unnecessary.
If you want to apply the page in a codebase: start by writing down your effect inventory and labeling each as derive vs orchestration.
The ones you can't label are the ones that will bite you later.