Skip to Content
React in a Week
Day 4 · State & escape hatches

Choosing the state structure

Five principles for shaping state well, and how React keeps or resets state based on position and key.

A well-shaped state is easy to update and hard to break. A badly shaped state forces you to keep several values in sync by hand, and sooner or later you forget one. A few minutes spent on the shape of your state saves hours of debugging.

Five principles

The React docs list five principles. You don't have to memorize them; each one is a way of removing duplication.

  1. Group related state. If two values always change together, such as x and y of a mouse position, store them in one object.
  2. Avoid contradictions. If two booleans can never both be true, use one value instead.
  3. Avoid redundant state. If you can calculate a value from props or other state, don't store it.
  4. Avoid duplication. Don't store the same object in two places.
  5. Avoid deep nesting. Deeply nested objects are painful to update immutably. Prefer flat data.

Avoid contradictions

Here is a form with two booleans. Nothing stops them from both being true, which would be a bug.

// Avoid: isSending and isSent could both be true
const [isSending, setIsSending] = useState(false);
const [isSent, setIsSent] = useState(false);

Replace them with one status value that can only be one thing at a time:

Live code· One status instead of two booleans

You can still have readable names like isSending, but they are calculated from status, so they can never disagree.

Avoid duplication: store the ID, not the object

Say you show a list of books and highlight the selected one. A natural first try is to store the whole selected book:

// Avoid: the selected book is a second copy of an item in `books`
const [books, setBooks] = useState(initialBooks);
const [selectedBook, setSelectedBook] = useState(initialBooks[0]);

Now if you rename a book in books, selectedBook still holds the old title. Store the ID and look the book up during render:

Live code· Store the selected ID

Edit the title of the chosen book. The "You picked" line updates instantly, because there is only one copy of each book.

Quick check

Which of these is redundant state?

State lives at a position in the tree

Here is something surprising. State is not stored "inside" a component function. React stores it next to the position where that component is rendered in the tree. As long as the same component type stays at the same position, React keeps its state.

Try it: click "Add one" a few times, then tick the checkbox.

Live code· Same position, same state

Sarah "inherits" Taylor's score. From React's point of view nothing changed: there is a Counter at the same spot both times, with a different prop. So it keeps the state.

Resetting state with a key

To tell React "this is a different counter, start fresh", give each one a different key. You used key for list items on Day 2. It works anywhere: when the key changes, React throws away the old component and its state and creates a new one.

{isPlayerA ? (
  <Counter key="Taylor" person="Taylor" />
) : (
  <Counter key="Sarah" person="Sarah" />
)}
Live code· Different key, fresh state

Now each player keeps a separate score from zero. You will use this trick again in the "You might not need an Effect" lesson to reset a whole form when the user picks a different item.

Try it

Reset the note when the book changes

This app shows a note editor for the selected book. Type a note for "Dune", then pick "Piranesi". The draft from Dune is still there. Fix it so each book starts with an empty draft, without adding any new state or Effects.

Live code· Challenge: reset with key
Show solution

Give NoteEditor a key that changes with the selected book:

<NoteEditor key={selected.id} book={selected} />

When selectedId changes, the key changes, so React creates a new NoteEditor with a fresh draft.

Day 4 progress

0/10

On this page