React: From Components to Production · State, Events and Effects
Structuring and Lifting State
Avoid redundant state, derive values, and lift state to the closest common parent.
Keep state minimal: don't store values you can derive during render (filtered lists, totals, full names). Avoid duplicated or contradictory state (e.g., isLoading + isError + isSuccess — use one status). When siblings need the same data, lift state up to their closest common parent and pass values and callbacks down.
type Status = "idle" | "loading" | "success" | "error";
export function ProductPage({ products }: { products: Product[] }) {
const [query, setQuery] = useState("");
const [status, setStatus] = useState<Status>("idle");
// Derived — no extra state, always in sync
const visible = products.filter((p) => p.name.toLowerCase().includes(query.toLowerCase()));
const total = visible.reduce((sum, p) => sum + p.priceCents, 0);
return (
<>
<SearchBox value={query} onChange={setQuery} />
<ProductList products={visible} />
<Summary count={visible.length} totalCents={total} />
</>
);
}
Common pitfalls
- Copying props into state and letting them drift out of sync.
- Multiple booleans that allow impossible combinations.
- Storing derived data and syncing it with effects.
Key points
- Derive instead of storing
- Use a single status value
- Lift state to the closest common parent
Further reading: react.dev: Choosing the state structure
Practice questions
Why is const [filtered, setFiltered] = useState(...) plus an effect a code smell?
Answer
Filtered data can be computed from the source list and query during render. Storing it adds an extra render and risks stale data.
What does 'lifting state up' mean?
Answer
Moving state from child components to their closest common parent so both children receive the same value via props and update it via callbacks.
Open in the app to mark lessons complete, take notes and track your progress.