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.