The `useState` Hook:Giving Components Memory

Discover React's fundamental hook. Learn to declare state, update it, and trigger UI re-renders to build interactive applications.

Lesson ProgressStep 1 of 10
import { useState } from 'react';
const [count,setCount] = useState(0);

You clicked 0 times

🏆
0 EXP

Hello! Let's learn how React components remember information and become interactive.

// How do we make a component interactive?

What is State?

In React, state is any data that your component "remembers" and that can change over time. Think of it as a component's personal memory. It holds information like user input in a form, whether a dropdown is open, or data fetched from an API.

When this state data changes, React knows it needs to update what's shown on the screen. This process of updating the UI in response to data changes is the core of React's interactivity.

System Check

What happens when a component's state changes?

Advanced Holo-Simulations

0 EXP

Log in to unlock these advanced training modules and test your skills.


Achievements

🏆
State Rookie

Successfully declare your first `useState` variable.

🚀
State Updater

Correctly use a setter function to update state.

✍️
Syntax Pro

Master the `useState` array destructuring syntax.

Mission: Build a Toggler

Create a component with a button. Use `useState` to create a state variable `isVisible` (default `true`). When the button is clicked, toggle the value of `isVisible` from `true` to `false` and back.

A.D.A. Feedback:

> System integrity looks stable. Code is valid.

Challenge: Order the Component

A component's structure matters. Drag the lines of code into the correct logical order from top to bottom.

const [count, setCount] = useState(0);
import React, { useState } from 'react';
return <p>{count}</p>;

Challenge: Complete the Syntax

Fill in the missing parts of the `useState` hook declaration.

[count,] =(0);

Consult A.D.A.

Community Holo-Net

Peer Project Review

Submit your "Toggler" component project for feedback from other React developers.

The Deep Dive: Mastering `useState`

The `useState` hook is the most fundamental and frequently used hook in React. It's the primary way you give your functional components "memory"—the ability to track changing data and re-render the UI in response. While a simple counter is a great start, mastering `useState` involves understanding its nuances, rules, and best practices.

1. The Core Syntax Explained

When you call `useState`, you are "hooking into" React's state mechanism.

import { useState } from 'react';

function MyComponent() {
  const [state, setState] = useState(initialState);
}
  • `useState(initialState)`: This is the hook itself. The argument `initialState` is the value you want the state to have on the *very first render*. This argument is **ignored** on all subsequent renders.
  • `[state, setState]` (Array Destructuring): `useState` returns an array with exactly two items. We use array destructuring to assign them to local variables.
    • The first item (which we named `state`) is the **current state value**.
    • The second item (which we named `setState`) is the **setter function**.

2. The Rules of Hooks

Hooks have two critical rules you must follow:

  • Only Call Hooks at the Top Level: Do not call `useState` inside loops, conditions, or nested functions. Always call it at the top level of your React function. This ensures that hooks are called in the same order on every render, which is how React keeps track of them.
  • Only Call Hooks from React Functions: Call them from your functional components or from custom hooks. Don't call them from regular JavaScript functions.

3. State Updates are Asynchronous

This is the most common "gotcha" for new React developers. When you call the setter function, it does **not** change the state variable immediately.

function handleClick() {
  setCount(count + 1); // Requests a re-render
  console.log(count);    // ❌ This will log the OLD count value!
}

React **batches** state updates for performance. It schedules a re-render with the new state value. The `count` variable will only be updated when the component *re-renders*. If you need to perform an action based on the new state, use the `useEffect` hook.

4. Functional Updates: The Safer Way to Update

What happens if you call `setCount` multiple times in one event handler?

function handleTripleClick() {
  setCount(count + 1); // e.g., if count is 0, setCount(0 + 1)
  setCount(count + 1); // e.g., if count is 0, setCount(0 + 1)
  setCount(count + 1); // e.g., if count is 0, setCount(0 + 1)
  // The count will only increase by 1, not 3!
}

Because `count` hasn't changed yet, all three calls use the same old value. To fix this, you can pass a **function** to the setter. This is called a "functional update." This function receives the pending state as its argument.

function handleTripleClick() {
  setCount(prevCount => prevCount + 1);
  setCount(prevCount => prevCount + 1);
  setCount(prevCount => prevCount + 1);
  // Now the count will correctly increase by 3!
}

**Best Practice:** Always use a functional update when your new state depends on the previous state.

5. State with Objects and Arrays: The Rule of Immutability

You must **never** mutate state directly. React determines if it needs to re-render by comparing the old state with the new state. If you mutate an object or array, the reference in memory remains the same, and React won't "see" the change.

❌ Bad Practice (Mutation)

const [user, setUser] = useState({ name: 'A', age: 20 });

function handleAgeUp() {
  user.age = 21; // 😱 MUTATION!
  setUser(user);   // React won't re-render
}

✔️ Good Practice (Immutability)

const [user, setUser] = useState({ name: 'A', age: 20 });

function handleAgeUp() {
  // Create a NEW object
  const newUser = { ...user, age: 21 };
  setUser(newUser);
}

Always create a **new** object or array (using the spread syntax `...` is common) and pass that new value to the setter function.

6. Lazy Initialization

What if your initial state is the result of an expensive calculation?

// This expensive function runs on EVERY re-render
const [data, setData] = useState(computeExpensiveValue());

To fix this, you can pass a **function** to `useState`. React will only call this function on the initial render to get the initial state.

// Pass a function (a "lazy initializer")
const [data, setData] = useState(() => {
  return computeExpensiveValue();
});
// This only runs ONCE!
Key Takeaway: `useState` is your tool for component memory. Respect its rules (top-level calls, immutability) and understand its nuances (asynchronous updates, functional updates) to build complex, bug-free, and performant React applications.

React State & `useState` Glossary

State
Data that a component "remembers" or manages internally. When a component's state changes, React will re-render that component to reflect the new data.
Hook
Special functions provided by React (always starting with `use`, e.g., `useState`, `useEffect`) that let you "hook into" React features like state and lifecycle methods from functional components.
Rules of Hooks
The two critical rules for using hooks: 1. Only call hooks at the top level of your component. 2. Only call hooks from React functional components or custom hooks.
`useState(initialState)`
The React Hook that lets you add state to a functional component. It takes one argument, the initial state, and returns an array with the current state and a setter function.
State Variable
The first element in the array returned by `useState`. This variable holds the current state value for that render. (e.g., `count` in `const [count, setCount] = ...`).
Setter Function
The second element in the array returned by `useState`. This is the function you must call to update the state and trigger a re-render. (e.g., `setCount` in `const [count, setCount] = ...`).
Re-render
The process where React calls your component function again to get the new description of the UI. This happens automatically after a state or prop change.
Immutability
The principle of never changing an object or array in place. When updating state, you must always create a *new* object or array, which is how React detects the change.
Functional Update
The act of passing a function to the state setter (e.g., `setCount(prevCount => prevCount + 1)`). This is the safest way to update state that depends on the previous state.
Lazy Initialization
The act of passing a function to `useState` (e.g., `useState(() => expensiveFn())`). React will only run this function on the initial render to calculate the initial state, improving performance.
Controlled Component
A form element (like an `<input>`) whose value is controlled by React state. The value is set by the state variable, and it's updated via the setter function in an `onChange` handler.

About the Author

Author's Avatar

TodoTutorial Team

Passionate developers and educators making programming accessible to everyone.

This article was written and reviewed by our team of senior React developers, who have years of experience building and scaling complex, state-driven applications.

Verification and Updates

Last reviewed: October 2025.

We strive to keep our content accurate and up-to-date. This tutorial is based on the latest React (v18+) specifications and is periodically reviewed to reflect industry best practices.

External Resources

Found an error or have a suggestion? Contact us!