Skip to Content
React in a Week
Day 3 · Interactivity

State: a component's memory

Use the useState Hook to give a component values that it remembers between renders.

Event handlers let you notice a click. Now you need to remember something because of it: how many times a button was pressed, what's typed in a box, which book is selected. A component needs a memory.

In React, that memory is called state. In this lesson you'll see why a regular variable isn't enough, and how useState fixes it.

When a regular variable isn't enough

Here is a counter that tries to use a normal let variable. Click the button a few times. The console says the number is going up, but the screen stays at 0.

Live code· A counter that doesn't work

Two things go wrong:

  1. Changing a local variable doesn't tell React to re-render. React has no idea anything changed, so it never draws the new value.
  2. Local variables don't survive between renders. Even if React did re-render, it would call App() again from the top, and let count = 0 would reset it to zero.

To update the screen, you need both: a value that is kept between renders, and a way to tell React "this changed, please render again."

Adding state with useState

The useState Hook gives you both. Here's the same counter, fixed.

Live code· A working counter

Let's read that key line slowly:

const [count, setCount] = useState(0);
  • useState(0) asks React for a piece of state whose initial value is 0. React only uses the initial value on the very first render.
  • It returns an array with exactly two things, which you unpack with array destructuring (from Day 1):
    • count, the current value of the state for this render;
    • setCount, a setter function that changes the value and tells React to re-render.
  • The names are up to you. The convention is [something, setSomething].

So when you click: handleClick calls setCount(1). React stores 1, then calls App() again. This time useState(0) returns [1, setCount], so the h1 shows 1.

More than one piece of state

A component can have as many state variables as it needs, of any type: numbers, strings, booleans, arrays, objects. Use one useState call per independent value.

Live code· Multiple state variables

Here index and showDetails change independently, so two separate state variables make sense. Notice that hasNext and photo are not state. They are calculated from index on every render. If you can compute something from existing props or state, don't store it in state too. You'll dig into this idea on Day 4.

The rules of Hooks

Functions whose names start with use are called Hooks. useState is the first of several you'll meet this week. Hooks are special: React keeps track of them by the order in which your component calls them. That gives two rules:

  • Only call Hooks at the top level of your component, not inside if statements, loops or nested functions.
  • Only call Hooks from React components (or from your own custom Hooks, which you'll write on Day 4).
Deep diveHow does React know which state is which?

You never pass a name or ID to useState, so how does React know that the first call is index and the second is showDetails?

It relies on call order. Internally, React keeps a list of state slots for each component on the screen. During a render, the first useState call gets slot 1, the second gets slot 2, and so on. As long as every render calls the Hooks in the same order, each call lines up with the right slot. That's why the rules above exist: a Hook inside an if would shift every slot after it.

State is private to each component

State belongs to a specific copy of a component on the screen. If you render the same component twice, each copy gets its own completely separate state. Click the counters below independently.

Live code· Each copy has its own state

The parent App doesn't know or control what count each Counter has. This is different from props, which a parent passes in. If two components ever need to share the same state, you lift it up to their common parent. That's a Day 4 topic.

Try it

Build a like button

Make the LikeButton remember whether it's liked. Clicking should toggle between "Like" and "Liked", and the button should get the liked class when liked.

Live code· Challenge: like button
Show solution
Live code· Solution: like button

A boolean in state plus setLiked(!liked) is the standard way to toggle something on and off. Don't forget to import useState from 'react'.

Try it

A character counter

Build a text box that shows how many characters have been typed, like "12 / 140". If the count goes over 140, add the over class to the counter. Hint: store the text in state and set it from onChange with e.target.value.

Live code· Challenge: character counter
Show solution
Live code· Solution: character counter

You store only the text. The count and the "over" flag are calculated from it during render. Passing value={text} makes this a controlled input, which you'll learn properly in the forms lesson.

Quick check

Why doesn't changing a regular let variable inside an event handler update the screen?

Quick check

You render a Counter component three times. How many separate count values exist?

Day 3 progress

0/6

On this page