Chapter 4 of 4
Lesser-Known Built-in Hooks
Three hooks that solve specific problems custom hooks often need.
useId: stable unique ids
Accessible markup needs ids that link labels to inputs. useId generates one that matches between the server render and the client, which Math.random() cannot do.
function PasswordField() {
const id = useId();
return (
<>
<label htmlFor={id}>Password</label>
<input id={id} type="password" aria-describedby={id + "-hint"} />
<p id={id + "-hint"}>At least 8 characters</p>
</>
);
}useDebugValue: labelling hooks in DevTools
function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(true);
useDebugValue(isOnline ? "Online" : "Offline");
return isOnline;
}useSyncExternalStore: subscribing to non-React state
When a value lives outside React - a browser API, a third-party store, an event emitter - this hook subscribes to it safely, including during concurrent rendering.
function useOnlineStatus() {
return useSyncExternalStore(
(callback) => {
window.addEventListener("online", callback);
window.addEventListener("offline", callback);
return () => {
window.removeEventListener("online", callback);
window.removeEventListener("offline", callback);
};
},
() => navigator.onLine, // value on the client
() => true // value during server rendering
);
}Write a debounce helper
Complete debounce so the returned function only calls fn after the given delay, restarting the timer on each call.