Separating State Variables

Class components had one this.state object, so everything went into it. Hooks let you call useState as often as you like, and the useful rule is to group values that change together and split values that do not.

One state object versus state grouped by what changes togetherJavaScript
function Box() {
  // one blob: every mouse move has to re-spread width and height to keep them
  const [state, setState] = useState({ left: 0, top: 0, width: 100, height: 100 });
  useEffect(() => {
    const onMove = e => setState(s => ({ ...s, left: e.pageX, top: e.pageY }));
    window.addEventListener('mousemove', onMove);
    return () => window.removeEventListener('mousemove', onMove);
  }, []);
}
function BetterBox() {
  const position = useWindowPosition();          // a custom hook, Section 2.10.1
  const [size, setSize] = useState({ width: 100, height: 100 });
}

Forgetting the spread in the first version silently deletes width and height, because useState never merges the way this.setState did. The second cannot lose anything, and it splits cleanly: the position logic moves into a custom hook (Extracting a Custom Hook) and BetterBox keeps only what it owns.

Keep values together when they genuinely travel together — the two ends of a range slider, the x and y of a point, a form's fields when you submit them as one object. Split them when one changes sixty times a second and the other twice an hour. The updater is the signal: if most of it is spread syntax carrying values you never touch, those values want their own useState.