State & Hooks

State is a component's memory. Hooks are the functions that give components state and other React features.

Why ordinary variables don't work

You might try to make a counter with a normal variable:

function Counter() {
  let count = 0;
  return (
    <button onClick={() => count++}>
      Clicked {count} times
    </button>
  );
}

This fails for two reasons. Local variables are reset every time the function runs, and changing one does not tell React to update the screen.

useState

The useState hook solves both problems. It gives you a value that React keeps between renders, and a setter function that updates the value and triggers a re-render:

import { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <button onClick={() => setCount(count + 1)}>
      Clicked {count} times
    </button>
  );
}

Each <Counter /> you place on the page keeps its own separate count.

State with forms

A common pattern is to keep form inputs in state. This is called a controlled input:

function NameForm() {
  const [name, setName] = useState("");

  return (
    <>
      <input
        value={name}
        onChange={e => setName(e.target.value)}
        placeholder="Your name"
      />
      <p>Hello, {name || "stranger"}!</p>
    </>
  );
}

Updating objects and arrays

Treat state as immutable. Don't modify it in place; create a new copy instead:

// โœ˜ Wrong: mutates the existing array
todos.push(newTodo);
setTodos(todos);

// โœ” Right: creates a new array
setTodos([...todos, newTodo]);

// โœ” Right: copies an object with one field changed
setUser({ ...user, name: "Grace" });

Sharing state between components

When two components need the same data, move the state up to their closest common parent and pass it down as props. This is called lifting state up:

function App() {
  const [query, setQuery] = useState("");

  return (
    <>
      <SearchBox query={query} onChange={setQuery} />
      <Results query={query} />
    </>
  );
}

Data flows down through props, and events flow up through callback functions. This one-way data flow makes React apps easier to reason about.

Other common hooks

HookWhat it's for
useStateStoring a value that changes over time and re-rendering when it does.
useEffectSyncing with something outside React, such as timers, subscriptions or browser APIs.
useRefHolding a value that doesn't trigger re-renders, or referencing a DOM element.
useContextReading shared data (such as a theme or the logged-in user) without passing props through every level.
useReducerManaging more complex state with a reducer function.

A quick look at useEffect

import { useState, useEffect } from "react";

function Clock() {
  const [time, setTime] = useState(new Date());

  useEffect(() => {
    const id = setInterval(() => setTime(new Date()), 1000);
    return () => clearInterval(id); // clean up when removed
  }, []); // [] = run once, when the component first appears

  return <p>{time.toLocaleTimeString()}</p>;
}

The rules of hooks

React relies on hooks being called in the same order on every render, which is why these rules exist. The official ESLint plugin checks them for you.