Trust is earned, not given

A different perspective

2023-12-05 · Projects

React.js, part 7: useDeferredValue and useId — the quiet hooks that fixed real bugs

Part 7from the React.js series · 20 parts in all

Not every useful hook arrives with a keynote. Two of them quietly removed whole classes of everyday problems: useDeferredValue gave back the responsive list without restructuring state, and useId made SSR-safe element ids a non-issue.

useDeferredValue: the lazy version of a transition

A transition needs you to own the state update. Often you do not — a value arrives as a prop, or from a component you cannot change. useDeferredValue lets you defer any value:

function Results({ query }) {
  // The list renders from the lagging value; typing stays instant because
  // React keeps the previous deferred value while the new one catches up.
  const deferred = useDeferredValue(query);
  const stale = deferred !== query;   // show a subtle "updating" state

  return (
    <div className={stale ? 'dim' : ''}>
      <BigList filter={deferred} />
    </div>
  );
}

Two subtleties: the deferred value must be a primitive or a stable reference (deferring a freshly built object defeats it), and the stale comparison is how you render the "hmm, updating" affordance without a loading spinner.

useId: ids that survive hydration

Accessible markup pairs <label for> with a matching input id. On the server you cannot generate a counter-based id safely — the client's counter would disagree and hydration would mismatch. useId returns a stable id that matches on both sides:

function EmailField() {
  const id = useId();
  return (
    <>
      <label htmlFor={id}>Email</label>
      <input id={id} type="email" aria-describedby={id + '-hint'} />
      <small id={id + '-hint'}>We only use this for receipts.</small>
    </>
  );
}

The generated value is opaque (it contains colons) — do not parse it, and do not use it as a CSS selector. Its only job is to be unique and hydration-stable, and as a bonus that makes it perfect for aria-* wiring in list rows.

Next: Server Actions — forms that post to the server without hand-writing a fetch, JSON body, and error channel.