State
State is the data that changes while the app runs — the text in a search box, the items in a cart. Deciding which component owns each piece of state is the heart of React design.
On this page · 6 min · Beginner
On this page
- Learning objectives
- Explanation
- Syntax
- Code example
- Step by step
- Real-world use
- Common mistakes
- Best practices
- Remember this
- Practice exercise
- Mini quiz
- Related topics
- My notes
- Reading time
- 6 min
- Difficulty
- Beginner
- Updated
- Oct 28, 2026
Related tutorials
Learning objectives#
- Tell state apart from props and derived values
- Keep state minimal
- Lift state up to share it between siblings
- Follow one-way data flow
Explanation#
Props come from the parent and are read-only. State belongs to a component and changes over time (with useState or useReducer). Changing state re-renders the component and its children.
Keep only the minimum in state. Anything you can compute from props or other state — totals, filtered lists, full names — is derived: calculate it during render.
If two siblings need the same data, move (lift) the state up to their closest common parent and pass it down as props, together with functions to change it.
Each piece of state has one owner — a single source of truth. Children receive values and callbacks; they never hold copies that can drift out of sync.
Not everything is state: constants belong outside the component, and values that never affect rendering can live in a ref.
Syntax#
function Parent() {
const [query, setQuery] = useState(''); // state lives here
return (
<>
<SearchBox query={query} onChange={setQuery} /> {/* writes */}
<Results query={query} /> {/* reads */}
</>
);
}
Code example: Lifting state up for a filterable list#
import { useState } from 'react';
const PRODUCTS = [
{ id: 1, name: 'Pen', price: 20 },
{ id: 2, name: 'Pencil', price: 10 },
{ id: 3, name: 'Notebook', price: 60 },
];
function SearchBox({ query, onQueryChange }) {
return (
<input
value={query}
onChange={e => onQueryChange(e.target.value)}
placeholder="Search"
aria-label="Search products"
/>
);
}
function ResultList({ items }) {
return <ul>{items.map(p => <li key={p.id}>{p.name} — ₹{p.price}</li>)}</ul>;
}
export default function FilterableList() {
const [query, setQuery] = useState(''); // the only state
const visible = PRODUCTS.filter(p => // derived, not stored
p.name.toLowerCase().includes(query.toLowerCase()));
const total = visible.reduce((sum, p) => sum + p.price, 0); // derived
return (
<>
<SearchBox query={query} onQueryChange={setQuery} />
<ResultList items={visible} />
<p>{visible.length} items, ₹{total} in total</p>
</>
);
}
Initially all three products are listed with “3 items, ₹90 in total”. Typing “pen” shows Pen and Pencil with “2 items, ₹30 in total”. Both the list and the summary always agree because they come from the same query.
This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.
Step-by-step explanation#
- query is the only state; it lives in the parent, FilterableList.
- SearchBox displays query and reports changes through onQueryChange — it owns nothing.
- visible and total are calculated during render, so they can never be out of date.
- PRODUCTS never changes, so it is a constant outside the component.
Real-world use case#
On a flight-search page, the date picker, passenger selector and results list all need the same search criteria. The criteria live in the page component and flow down as props.
Common mistakes#
Keeping filteredItems in state means updating it everywhere query or items change — and forgetting once causes bugs. Compute it.
useState(props.value) only uses the first value; later prop changes are ignored. Use the prop directly or lift the state.
Putting every input’s text in App re-renders the whole app on each keystroke. Keep state as close as possible to where it is used.
Best practices#
- Minimal state, maximum derivation
- One owner per piece of state
- Lift state only as high as needed
- Pass setters or callbacks down, values down
Remember this#
Props = given to you. State = yours and changing.
If you can calculate it, do not store it.
Siblings share state → lift it to the parent.
Practice exercise#
Your task: Two components, TemperatureInput (Celsius) and FahrenheitDisplay, must stay in sync. Lift the Celsius state into a parent and show both.
Need a hint?
Show solution
import { useState } from 'react';
function TemperatureInput({ celsius, onChange }) {
return <input type="number" value={celsius} onChange={e => onChange(Number(e.target.value))} aria-label="Celsius" />;
}
function FahrenheitDisplay({ celsius }) {
return <p>{celsius}°C = {celsius * 9 / 5 + 32}°F</p>;
}
export default function Converter() {
const [celsius, setCelsius] = useState(25);
return (
<>
<TemperatureInput celsius={celsius} onChange={setCelsius} />
<FahrenheitDisplay celsius={celsius} /> {/* 25°C = 77°F */}
</>
);
}
Mini quiz#
A cart total can be computed from the cart items. Where should it live?
Want more? Practise all React MCQs · React interview questions
My notes#
Saved in this browser.