React Data Flow & the useEffect Footgun React's data flow is meant to be simple: props flow down, events flow up, and the UI is a function of state. exists as the one deliberate escape hatch from that model — a place to synchronize with something outside React, like a subscription, a manual DOM measurement, or an imperative browser API. The footgun is that its shape — "run this code after render, optionally again when some values change" — looks exactly like a place to put any code that should happen after a state change, including code that has nothing to do with anything outside React at all. What useEffect is actually for The contract is narrower than it looks: synchronize React with a system React doesn't control. A WebSocket connection. A third-party widget that needs manual initialization. . A subscription to something outside the component tree. In every one of these, the point of the effect is that the outside world needs to be told about a change — not that some other piece of React state needs computing. The footgun: using an effect to derive state The misuse is subtler than that, and far more common. It's using an effect to compute one piece of state from another, purely inside React, with nothing external involved at all: This runs, and it's wrong in a way that's easy to miss because nothing crashes. Every time or changes, React renders once with the stale , commits that render, then runs the effect, calls , and renders again with the correct value. Users see a flash of stale data on every keystroke, purely because the derivation was routed through an extra render cycle it never needed. The fix removes the effect entirely: Or, if the computation is expensive enough to be worth caching: Either way, there's no state to keep in sync, because there's no second variable at all — just a value computed fresh from the ones that already exist. The chain reaction version The real damage shows up when this pattern compounds. A second derived value gets the same treatment — an effect watching a few fields, setting an state. Then a third effect is added that watches and sets something else. Now effects are triggering each other, each one causing its own extra render, and tracing "why did this render four times" means reading a chain of effects that each only make sense in terms of the next one. This is the same shape of problem the previous lesson covered as nested subscribes in RxJS — reactive callbacks chained together instead of one clear, traceable computation. The principle that transfers Angular's version of this chapter already named the Angular-side equivalent: subscribing inside a subscribe, or chaining calls that each react to another's output. Both frameworks have the same underlying temptation — reactive primitives are so easy to chain that it's tempting to build a cascade of them instead of asking whether the value could just be computed directly, once, in the place that needs it. The fix is identical in spirit on both sides: collapse the chain into one direct computation, and reserve the reactive primitive — , — for the genuine case of talking to something outside the framework's own state. The Trade-off: Is Every useEffect Actually Necessary? Reaching for the moment you want "something to happen when this value changes" feels natural — it's right there in the mental model most people bring from lifecycle methods. The cost of that reflex only shows up once derived values start compounding. The pain, concretely v1: A component derives from and using a . Works. Causes one extra render per keystroke, unnoticed at this scale. v2: A second derived value — , computed from several form fields — gets the identical treatment: its own effect, its own extra render. v3: One derived effect's setter accidentally triggers a second effect that watches its output, creating a two-step chain. Occasionally, under specific input timing, this produces a render loop severe enough that React's development-mode warning catches it — but only in dev mode, not in the production build that shipped last week. v4: A genuine external-sync effect — a WebSocket subscription — gets added to the same component. It's now one effect among four, and when it actually needs a bug fix, nobody can find it quickly among the effects that are really just doing arithmetic on props. Side-by-side Deriving state via useEffect Computing directly during render --- --- --- Extra renders / visible staleness Yes — one stale render, then a corrected one None — the value is correct on the first render Traceability of where a value comes from Buried in an effect's dependency array Visible as a plain expression, right where it's used Risk of a cascade Real — one effect's setState can trigger another's dependencies None — there's no chain of effects to cascade Correct use for genuine external sync (sockets, subscriptions) This is what effects are actually for N/A — still needs an effect, and now it's easy to find The signal to switch The moment an effec…