React Forum
Blog & Guide Responses
Jan 18, 2023 · 04:00 AM

React useEffect - discussion (2023-01-18)

Started by Noah Vance
externalreact.devuseEffecteffectsdependenciescleanupevidence

The useEffect reference is a good reminder that effects are a boundary to the outside world, but in real apps effects often become the place where people "fix" state after render. I'm curious what conventions people use to keep effects small, observable, and not secretly responsible for correctness.

Read the reference

What rules do you use for dependency lists (always include, stable refs, split effects, etc.)? How do you make effect behavior visible so timing isn't the only debugging tool? When do you replace an effect with a remount boundary or an explicit event/mutation path?

Comments (12)

Back to latest
Marisol Vega
Jan 22, 2023 · 08:40 AM

Our best rule: if the effect exists to make render correct, it doesn't belong in an effect.

Effects are for outside synchronization; derivations should be part of the state contract.

Dmitri Kline
Feb 02, 2023 · 08:18 PM

Splitting effects made dependency lists sane for us. Instead of one mega effect with a giant list, we have 2-3 small effects with a clear purpose and visible evidence:

tsx
function useTitleEvidence(title: string) {
  useEffect(() => {
    document.title = title;
  }, [title]);
}

function useSubscriptionEvidence(source: string, onData: (v: any) => void) {
  useEffect(() => {
    const sub = subscribe(source, onData);
    return () => sub.unsubscribe();
  }, [source, onData]);
}
Keira Santos
Feb 16, 2023 · 06:10 AM

Counterpoint: some effects *are* about correctness because the outside world is part of correctness (focus, scroll, media).

We just force ourselves to render a debug line about it so it's not invisible.

Noah Vance
Feb 19, 2023 · 12:12 PM

Totally. My issue is hidden correctness, not correctness in general.

If focus is a feature, it should be observable and intentional.

Grace McCoy
Mar 05, 2023 · 07:33 PM

We added a convention that every effect has a corresponding evidence key (status, lastRunAt, lastCleanupAt).

Even if it's behind a debug flag, it prevents "did it run?" from being a guess.

Jasper Nguyen
Mar 22, 2023 · 04:48 AM

Dependency lists: we include everything and then make functions stable if needed.

If you exclude deps "because it works", you're betting on timing.

Nina Kapoor
Apr 11, 2023 · 04:55 PM

A practical pattern for visible effect behavior is to store an effect lane in state and render it:

ts
type EffectLane = 'idle' | 'running' | 'cleanup' | 'error';
doc.write('subscriptionLane', 'running');

If you can't observe the lane, effect bugs feel like ghosts.

Camille Duarte
May 01, 2023 · 08:03 AM

We replaced a bunch of "reset on id change" effects with keyed remount boundaries.

It's blunt but deterministic, and it made tests much easier.

Owen Price
May 24, 2023 · 10:18 PM

A misunderstanding I had: I thought Strict Mode double-invoke meant effects are unreliable.

Now I treat it as a stress test for cleanup and idempotency.

Benji Rios
Jun 19, 2023 · 02:10 AM

Counterpoint: sometimes remount boundaries are used as a hammer and they hide real lifecycle bugs.

We try to reserve remounting for identity change and keep other fixes as explicit cleanup logic.

Dmitri Kline
Jun 22, 2023 · 06:55 AM

Agree. If you remount to avoid reasoning, you can create performance problems and new bugs.

But as an identity boundary, remounting is a great tool.

Lina Ortega
Jul 15, 2023 · 11:44 AM

Docs tie-in: effects become a lot less scary when you adopt a consistent evidence posture for render + fetch + effects.

Related: Testing and Debugging and Performance and Rendering.

Amina Farouk
Aug 18, 2023 · 03:03 AM

We also limit effect bodies to a single responsibility (subscribe, measure, sync title).

If an effect starts doing three things, it becomes impossible to reason about dependencies.

Tomas Rivera
Sep 28, 2023 · 03:20 PM

The biggest unlock for me was treating effects as a contract boundary: declare what they sync, render evidence, and keep the rest deterministic.