Principles for structuring state
The goal behind these principles is to make state easy to update without introducing mistakes. Removing redundant and duplicate data from state helps ensure that all its pieces stay in sync.
Group related state
if some two state variables always change together, it might be a good idea to unify them into a single state variable. Then you won’t forget to always keep them in sync,
Another case where you’ll group data into an object or an array is when you don’t know how many different pieces of state you’ll need. For example, it’s helpful when you have a form where the user can add custom fields.
If your state variable is an object, remember that you can’t update only one field in it without explicitly copying the other fields.
Avoid contradictions in state
Since isSending and isSent should never be true at the same time, it is better to replace them with one status state variable that may take one of three valid states: ‘typing’ (initial), ‘sending’, and ‘sent’.
You can still declare some constants for readability:
const isSending = status === 'sending'; const isSent = status === 'sent';
But they’re not state variables, so you don’t need to worry about them getting out of sync with each other.
Avoid redundant state
If you can calculate some information from the component’s props or its existing state variables during rendering, you should not put that information into that component’s state.
This form has three state variables: firstName, lastName, and fullName. However, fullName is redundant. You can always calculate fullName from firstName and lastName during render, so remove it from state.
Here, fullName is not a state variable. Instead, it’s calculated during render:
const fullName = firstName + ‘ ‘ + lastName;
As a result, the change handlers don’t need to do anything special to update it. When you call setFirstName or setLastName, you trigger a re-render, and then the next fullName will be calculated from the fresh data.
Don’t mirror props in state
A common example of redundant state is code like this:
function Message({ messageColor }) {
const [color, setColor] = useState(messageColor);Here, a color state variable is initialized to the messageColor prop. The problem is that if the parent component passes a different value of messageColor later (for example, ‘red’ instead of ‘blue’), the color state variable would not be updated! The state is only initialized during the first render.
This is why “mirroring” some prop in a state variable can lead to confusion. Instead, use the messageColor prop directly in your code. If you want to give it a shorter name, use a constant
This way it won’t get out of sync with the prop passed from the parent component.