useEffectEvent / stable “latest” callback

useEffectEvent / stable “latest” callback

What React is solving

Long-running subscribers (intervals, analytics, data channels) should not re-subscribe every time unrelated state changes, but their handler still needs to read fresh props/state. React 19’s useEffectEvent formalizes a stable function identity whose body always sees latest values.

The same idea in vanilla: keep a mutable ref object whose .current field you replace on every “render” (paint) with the latest function; the subscriber only holds a stable wrapper that calls latest.current().

React (reference)

const onTick = useEffectEvent(() => {
  console.log(count);
});

(Exact API evolves; the invariant is stable subscriber surface + latest closure.)


Vanilla implementation

latest.current is reassigned every paint so it always closes over the current count. A button simulates “something that subscribed once” by invoking latest.current() — no stale closure.

export function mountLesson09(host) {
  let count = 0;
  const latest = { current: () => 0 };

  function paint() {
    host.replaceChildren();
    latest.current = () => count;

    const label = document.createElement('p');
    label.textContent = `count: ${count}`;

    const inc = document.createElement('button');
    inc.type = 'button';
    inc.textContent = 'increment';
    inc.addEventListener('click', () => {
      count += 1;
      paint();
    });

    const read = document.createElement('button');
    read.type = 'button';
    read.textContent = 'subscriber reads latest() via ref';
    read.addEventListener('click', () => {
      const out = document.createElement('p');
      out.textContent = `latest(): ${latest.current()}`;
      host.append(out);
    });

    host.append(label, inc, read);
  }

  paint();
  return () => host.replaceChildren();
}

Mapping

IdeaVanilla
Latest logiclatest.current = () => count each paint
Stable subscriberany code that only calls latest.current()

Not in this lesson

Exact useEffectEvent runtime integration. See ../REQUIREMENTS.md.