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 wasn't wrong — the timing of when we read it was."

The Bug: a Zero-Width Waveform

The waveform needs to know its container's pixel width to draw. The obvious React move is a ref plus a mount-time useEffect with an empty dependency array: on mount, read ref.current.offsetWidth, set the width. It rendered at zero. The reason is subtle and worth internalizing: when the target is conditionally rendered (it only appears after a track loads), the mount-time effect runs while the element is still absent. The effect reads a null ref, sets width to zero, and — because its dependency array is empty — never runs again to correct itself.

The Fix: a Callback Ref

A callback ref is a function React calls when the node attaches and again when it detaches. Put the measurement inside it, and it fires at exactly the right moment — when the element actually exists — instead of at a mount that happened too early. For ongoing size changes, attach a ResizeObserver from inside that same callback. The rule of thumb: a mount-time useEffect assumes the ref is already populated; for anything conditionally rendered or late-appearing, use a callback ref that fires on attach.

The Sister Lesson: Don't Re-Debug Verified Code

The same engine had a different kind of scare worth pairing here. Mid-session, the sound seemed to stop. The instinct was to dive back into the scheduler math — but that math was already traced cold and verified (it drives a hardware synth in time). The real cause was transient state that a reload cleared; no code change fixed it because no code was broken. The discipline: when verified, math-correct code suddenly 'fails' with a vague symptom, suspect transient state first — reload, reproduce, isolate — before re-debugging the part you already proved correct. Re-suspecting solid code is how you burn an afternoon chasing a ghost.

Code

Measure with a callback ref, not useEffect + ref·typescript
// WRONG: mount-time effect runs while the conditional node is still null.
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
  setWidth(ref.current?.offsetWidth ?? 0);   // ref.current === null at mount
}, []);                                        // never re-runs -> width stays 0

// RIGHT: a callback ref fires WHEN the node attaches (and on detach).
const measureRef = useCallback((node: HTMLDivElement | null) => {
  if (!node) return;
  setWidth(node.offsetWidth);                  // runs on attach, correct width
  const ro = new ResizeObserver(() => setWidth(node.offsetWidth));
  ro.observe(node);                            // and keeps it correct on resize
}, []);
// Use measureRef for anything conditionally rendered or late-appearing.

External links

Exercise

Find a place in a UI where something is measured or initialized on mount but the target might not exist yet (a modal, a tab, a lazy-loaded panel). Ask: does the measurement assume the element is present at mount? If the element appears conditionally, rewrite it as a callback ref that fires on attach — and notice how many 'it renders at zero the first time' bugs that pattern quietly fixes.
Hint
The smell is 'works after a resize or a re-render, broken on first paint.' That's a mount-time read of an element that wasn't there yet. Callback ref fires when it IS there, so the first paint is correct too.

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.