Skip to content
C.W.K.
Stream
Lesson 05 of 05 · published

Measure With a Callback Ref

~12 min · react, callback-ref, resize-observer

Level 0Cold Ash
0 XP0/33 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"The waveform rendered at zero width. The ref was not wrong; the read happened at the wrong time."

The Bug: a Zero-Width Waveform

The waveform needs its container's pixel width. A ref plus a mount-time useEffect with an empty dependency array reads once when the component mounts. If the target node appears only after a track loads, that effect can run before the node exists. ref.current is null, and the empty dependency array means the effect never runs when the node later attaches.

The Fix: a Callback Ref With Symmetric Cleanup

A callback ref runs when the node actually attaches, which makes it a good place to measure conditional DOM. If the size can change, attach a ResizeObserver there. Setup alone creates a second bug, though: the observer must stop when the node detaches or the ref changes. React 19 lets a ref callback return a cleanup function, so return ro.disconnect(). The attach and detach paths then remain symmetric, including React Strict Mode's extra development setup-and-cleanup cycle.

The Sister Lesson: Verification Changes Priority

The same engine once seemed to lose sound mid-session. Already-verified scheduler math ranked below transient state and the current signal path, and the evidence did lead to state cleared by a reload. The rule is not 'never revisit verified code.' Verification lowers diagnostic probability; it does not acquit code forever. Reproduce from a known initial state, inspect observable state, and isolate the boundary the symptom crosses. If the evidence points back to verified code, inspect it again.

Code

Measure with a callback ref, not useEffect + ref·typescript
// WRONG: the mount effect runs once, before the conditional node exists.
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
  setWidth(ref.current?.offsetWidth ?? 0);
}, []);

// React 19: measure on attach and disconnect the observer on detach.
const measureRef = useCallback((node: HTMLDivElement | null) => {
  if (!node) return;

  const measure = () => setWidth(node.offsetWidth);
  measure();

  const ro = new ResizeObserver(measure);
  ro.observe(node);

  return () => ro.disconnect();
}, []);

External links

Exercise

Find a UI node that may not exist at component mount: a modal, tab, or lazy panel. Measure it with a callback ref on attach, and return cleanup for every observer or listener it creates. In Strict Mode, verify that setup and cleanup counts balance.
Hint
'Works after resize but not at first paint' suggests an early read. Callbacks multiplying each time you revisit the view suggest missing cleanup. Inspect attach and detach as one pair.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue
💛 by Pippawarm

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.