The useEffect you didn't need

ReactfrontendTypeScript

Reviewed a component this week that synced one piece of state into another with an effect:

const [items, setItems] = useState<Item[]>([]);
const [count, setCount] = useState(0);

useEffect(() => {
  setCount(items.length);
}, [items]);

This is a bug waiting to happen and an extra render every time. count is not independent state. It is items.length. Deriving it during render deletes the whole problem:

const [items, setItems] = useState<Item[]>([]);
const count = items.length;

The rule I keep coming back to: if you can compute a value from existing state or props during render, it is not state, and it does not belong in an effect. Effects are for synchronizing with something outside React, a subscription, the DOM, a network request. They are not for keeping two useState values in agreement.

Most of the useEffect calls I delete fall into this category. The component gets shorter, the extra render disappears, and a class of “why is this stale” bugs stops being possible.