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);
4
5 useEffect(() => {
6 // Fetch messages from an API...
7 const fetchedMessages = ["Hello", "World"];
8
9 setMessages(fetchedMessages);
10 setTotal(fetchedMessages.length);
11 }, []);
12
13 return (
14 <h1>Messages ({total})</h1>
15
16 // 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([]);
3
4 useEffect(() => {
5 // Fetch messages from an API...
6 const fetchedMessages = ["Hello", "World"];
7
8 setMessages(fetchedMessages);
9 }, []);
10
11 return (
12 <h1>Messages ({messages.length})</h1>
13
14 // 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.