React Forum
Blog & Guide Responses
Aug 20, 2022 · 04:00 AM

Synchronizing with Effects - discussion (2022-08-20)

Started by Rowan Pierce
externalreact.deveffectssynccleanupsignals

Effects are supposed to be for synchronization, but we keep using them as a place to patch app state after the fact.

Read the article

What effect patterns do you actually like in real apps (subscriptions, DOM, timers) and what do you ban? Do you have conventions that make effects debuggable (naming, rendered signals, strict cleanup discipline)? How do you migrate a sync effect into an explicit contract (route keys, derived evidence)?

Comments (10)

Back to latest
Maya Henson
Aug 20, 2022 · 04:06 AM

Timers and listeners, fine. Effects that fix state after render are always a regret.

Dmitri Kline
Aug 20, 2022 · 04:14 AM

We treat effects like tiny state machines with a rendered signal: start, active, cleanup.

It's not to spam logs, it's to make timing visible in screenshots and route-flow tests.

Related: Testing and Debugging and Resilience Drills (Deep Dive #3).

Salma Qureshi
Aug 20, 2022 · 04:22 AM

We standardized a helper and it made reviews easier (it returns a status you can render):

tsx
function useNamedEffect(name: string, fn: () => void | (() => void), deps: unknown[]) {
  const [status, setStatus] = useState<'start' | 'active' | 'cleanup'>('start');
  useEffect(() => {
    setStatus('start');
    const cleanup = fn();
    setStatus('active');
    return () => {
      setStatus('cleanup');
      cleanup?.();
    };
  }, deps);
  return { name, status } as const;
}

// usage
const effect = useNamedEffect('presence:socket', () => subscribe(), [roomId]);
return <div data-effect={effect.name} data-status={effect.status} />;
Hana Saito
Aug 20, 2022 · 04:31 AM

Counterpoint: I worry signals normalize effects by making them feel safe.

The best fix is still deleting the effect and making the contract explicit. Signals are a bandage, not a design.

Rowan Pierce
Aug 20, 2022 · 04:36 AM

I agree. The signals are mostly for the effects we can't avoid (subscriptions, analytics).

For sync effects, the goal is to migrate to route keys and derived evidence.

Hana Saito
Aug 20, 2022 · 04:39 AM

That framing helps. If it's unavoidable external sync, make it visible. If it's internal sync, delete it.

Keira Santos
Aug 20, 2022 · 04:44 AM

The worst is URL-sync effects. They always fight back/forward. If it should be shareable, it should be route truth.

Theo Bennett
Aug 20, 2022 · 04:52 AM

Cleanup discipline matters more than deps for us.

A leaky subscription is the kind of bug you can feel but can't explain without something visible.

Amina Farouk
Aug 20, 2022 · 05:03 AM

We had an effect that would settle derived state after navigation and it caused flicker only on back/forward. Deleting it and storing the derived evidence in the doc fixed both flicker and explainability.

tsx
const doc = useRouteDoc('inbox');
const threadId = doc.read('threadId');
const derived = useMemo(() => buildDerived(threadId), [threadId]);
doc.write('derivedEvidence', derived);
return <div data-thread={threadId} data-derived="stored" />;
Jules Kwon
Aug 20, 2022 · 05:15 AM

If you keep an effect, name it. If you can't name it, you can't review it.

Inez Park
Aug 20, 2022 · 05:27 AM

Strict Mode doubling effects is a great audit. Anything that breaks was relying on timing, not contracts.

Benji Rios
Aug 20, 2022 · 05:41 AM

If an effect changes behavior and nothing in the UI can prove it happened, you're debugging a ghost.