DEV Community

Cover image for Stop Putting Everything in useState
Yasin Demir
Yasin Demir

Posted on • Originally published at ysndmr.com

Stop Putting Everything in useState

There's a pattern I see constantly: every piece of UI state gets its own useState, the component re-renders on every interaction, someone adds useMemo to fix the performance, and the whole thing gets progressively harder to follow.

Most of that useState wasn't necessary.

useState is for values React needs to track in order to render correctly. Not every value in a component qualifies.

Three things that routinely end up in state when they shouldn't:

Derived values. If you have items in state and you're also storing filteredItems in state, you have two sources of truth that can diverge. filteredItems isn't state, it's a computation.

// what I see
const [items, setItems] = useState([]);
const [filteredItems, setFilteredItems] = useState([]);

// what it should be
const [items, setItems] = useState([]);
const filteredItems = useMemo(
  () => items.filter(item => item.active),
  [items]
);
Enter fullscreen mode Exit fullscreen mode

Values you only read in event handlers. If a value never influences what gets rendered, it doesn't need to trigger a re-render. useRef holds it without the overhead. A closure variable sometimes works too.

Every form field. Controlled inputs are occasionally the right call. More often, you're adding a re-render on every keystroke for no benefit. Reading from FormData on submit, or using an uncontrolled input with a ref, covers most cases without the noise.

The check I use before reaching for useState: if this value changes and nothing in the rendered output needs to change, it's probably not state. If it can be computed from something that is state, it definitely isn't.

React re-renders fast, but not free. The easiest performance wins don't come from memoizing expensive renders — they come from not rendering when you don't need to.


Originally published on ysndmr.com.

Top comments (0)