🚀 LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
🎓 COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
REFERENCEreact

react Documentation

LOADING ENGINE...

State

AI & DATA SCIENCE // state

State is data that's local to a component and can change over time, causing React to automatically re-render the component whenever it's updated.

Syntax

const [value, setValue] = useState(initialValue);

Deep Dive Course

Unlike props, which are passed in from a parent and are read-only, state is owned and managed entirely by the component itself, created with the useState hook in modern function components, and updated exclusively through its paired setter function, never by directly reassigning the state variable. Calling the setter function doesn't just update the value, it also tells React that the component, and its children, need to re-render, so the UI stays in sync with the current state — this is the fundamental mechanism that makes a React component reactive to changing data.

1Understanding State

Unlike props, which are passed in from a parent and are read-only, state is owned and managed entirely by the component itself, created with the useState hook in modern function components, and updated exclusively through its paired setter function, never by directly reassigning the state variable. Calling the setter function doesn't just update the value, it also tells React that the component, and its children, need to re-render, so the UI stays in sync with the current state — this is the fundamental mechanism that makes a React component reactive to changing data.

💡

Never modify a state variable directly, like pushing into a state array or assigning to a state object's property — always call the setter function with a new value or a new object/array, since React specifically relies on that setter call to know a re-render is needed.

editor.html
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      Clicked {count} times
    </button>
  );
}
localhost:3000

2Practical Example

Here is a real-world application of State showing how it is used in production React code.

editor.html
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  const incrementTwice = () => {
    setCount(prev => prev + 1);
    setCount(prev => prev + 1);
  };
  return <button onClick={incrementTwice}>Count: {count}</button>;
}
localhost:3000

3Best Practices

Follow these guidelines when working with State:

1. Always update state through its setter function, never by mutating the state variable directly, since React needs the setter call to trigger a re-render

2. Keep state as minimal and localized as possible, lifting it up to a shared parent only when multiple components genuinely need access to the same value

3. Use the functional updater form, setCount(prev => prev + 1), when a new state value depends on the previous one, to avoid subtle bugs from stale values in rapid successive updates

⚠️

Tip: Never modify a state variable directly, like pushing into a state array or assigning to a state object's property — always call the setter function with a new value or a new object/array, since React specifically relies on that setter call to know a re-render is needed.

editor.html
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      Clicked {count} times
    </button>
  );
}
localhost:3000

4Lifting State Up

Two sibling components can't directly share state, since each holds its own independent instance even if they call the same hook. When two components need to stay in sync, move ('lift') the state to their closest common parent, which owns it and passes both the value and a way to update it down as props to each child.

editor.html
function Parent() {
  const [query, setQuery] = useState('');
  return (
    <>
      <SearchInput query={query} onChange={setQuery} />
      <ResultsList query={query} />
    </>
  );
}
localhost:3000

Examples

Example 01Basic Usage
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      Clicked {count} times
    </button>
  );
}
Example 02Advanced Example
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  const incrementTwice = () => {
    setCount(prev => prev + 1);
    setCount(prev => prev + 1);
  };
  return <button onClick={incrementTwice}>Count: {count}</button>;
}

Best Practices

  • Always update state through its setter function, never by mutating the state variable directly, since React needs the setter call to trigger a re-render
  • Keep state as minimal and localized as possible, lifting it up to a shared parent only when multiple components genuinely need access to the same value
  • Use the functional updater form, setCount(prev => prev + 1), when a new state value depends on the previous one, to avoid subtle bugs from stale values in rapid successive updates
  • Lift state to the closest common parent of the components that need it, rather than reaching for context or a global store before it's actually necessary

Interview Question

Why does calling setCount(count + 1) twice in a row inside the same event handler only increment the count by 1, while setCount(prev => prev + 1) called twice increments it by 2?

Hint: Think about whether 'count' inside the event handler is a fixed snapshot from that render, or something that updates immediately after each setCount call.

Within a single render, count is a fixed, unchanging value captured from that render's closure — calling setCount(count + 1) twice in a row both times reads that exact same original count value and schedules the exact same new value, count + 1, as the update, so the second call doesn't actually build on the first, it just redundantly schedules an identical result. The functional updater form, setCount(prev => prev + 1), instead receives the most up-to-date pending state value at the moment React actually applies that specific update, rather than the stale value captured in the render's closure, so each successive functional update correctly builds on the previous one's result within that same batch. This distinction is exactly why the functional updater form is recommended whenever a new state value needs to be computed from the previous one, especially when multiple updates might happen close together.

Exercises

MediumPractice using State in a real scenario.
View Solution
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      Clicked {count} times
    </button>
  );
}

Frequently Asked Questions

Why does calling setCount(count + 1) twice in a row inside the same event handler only increment the count by 1, while setCount(prev => prev + 1) called twice increments it by 2?

Within a single render, count is a fixed, unchanging value captured from that render's closure — calling setCount(count + 1) twice in a row both times reads that exact same original count value and schedules the exact same new value, count + 1, as the update, so the second call doesn't actually build on the first, it just redundantly schedules an identical result. The functional updater form, setCount(prev => prev + 1), instead receives the most up-to-date pending state value at the moment React actually applies that specific update, rather than the stale value captured in the render's closure, so each successive functional update correctly builds on the previous one's result within that same batch. This distinction is exactly why the functional updater form is recommended whenever a new state value needs to be computed from the previous one, especially when multiple updates might happen close together.

Related Functions

usestatepropsrendering