How to prevent derived state in React
Today, we'll look at how to prevent derived state in React. This is an anti-pattern that is common amongst beginners, and something that can catch experienced developers out and become an issue at scale.
Table of contents
What is derived state?
Derived state is state that can be derived from an existing piece of state, or state which is derived from props.
Examples
The chances are that if a value can be derived from an existing piece of state, it's probably not state (and shouldn't be stored as such).
A basic example is some state containing messages.
1const Messages = () => {2 const [messages, setMessages] = useState([]);3 const [total, setTotal] = useState(0);45 useEffect(() => {6 // Fetch messages from an API...7 const fetchedMessages = ["Hello", "World"];89 setMessages(fetchedMessages);10 setTotal(fetchedMessages.length);11 }, []);1213 return (14 <h1>Messages ({total})</h1>1516 // Render the list of messages...17 );18};
This is a form of derived state and should be avoided where possible. We shouldn't store the total amount of messages in state as it can be derived from messages
(an existing piece of state). To prevent this, we would compute this value at render.
Let's see what that looks like:
1const Messages = () => {2 const [messages, setMessages] = useState([]);34 useEffect(() => {5 // Fetch messages from an API...6 const fetchedMessages = ["Hello", "World"];78 setMessages(fetchedMessages);9 }, []);1011 return (12 <h1>Messages ({messages.length})</h1>1314 // Render the list of messages...15 );16};
That's better. We are now calculating the total message count at render instead. This makes our code more clean and concise and prevents storing unnecessary derived state.
Why avoid derived state?
Derived state can cause silent, hard to trace issues in an application which can prove difficult to find and fix. Whilst deriving state is often unnecessary, it also goes against the single source of truth principle.
For example, the messages in the above example could be changed elsewhere (in another component or by another side effect) and the derived total state wouldn't update. On the contrary, it would update if it was calculated from the messages
state at render.
Performance
If computing a value at render is expensive and is actually causing performance issues, then consider memoization.
Conclusion
Put simply, if you can compute a value from existing state, it probably isn't state and you should probably compute it at render instead.
Hopefully this you enjoyed this article and it has helped to offer an alternate solution to deriving state.