Kodokon kodokon.com

Lifting state up and sharing data

Place each piece of state in the closest common ancestor to share data between several components without duplicating it.

7 min · 3 questions

Open this lesson in Kodokon

Two sibling components need to display the same data: a search field and the list it filters, for example. Local state in each one cannot work, because they cannot see each other. The solution is to lift the state up into their closest common ancestor: the parent owns the data and hands it back down through props.

JSX
import { useState } from "react";

function ProductPage({ products }) {
  const [query, setQuery] = useState("");

  return (
    <>
      <SearchBar query={query} onChange={setQuery} />
      <ProductList products={products} query={query} />
    </>
  );
}
The parent owns query and shares it with both children.

The children then become controlled components: they receive the value read-only and, to change it, call the callback function provided by the parent. Data flows down through props, change requests flow up through functions. This one-way flow makes the application's behavior easy to follow.

JSX
function SearchBar({ query, onChange }) {
  return (
    <input
      value={query}
      onChange={(e) => onChange(e.target.value)}
      placeholder="Search for a product"
    />
  );
}

function ProductList({ products, query }) {
  const visible = products.filter((p) =>
    p.name.toLowerCase().includes(query.toLowerCase())
  );
  return (
    <ul>
      {visible.map((p) => <li key={p.id}>{p.name}</li>)}
    </ul>
  );
}
SearchBar requests the change, ProductList reflects it.

Every piece of data must have a single source of truth. If two components each store their own copy of the same state, the copies will eventually diverge: one gets updated, the other does not, and the interface becomes inconsistent. Lifting state up removes that risk. When the data has to cross many levels, composition (next lesson) and then context lighten the prop passing.

Knowledge check

Make sure you remember the key points of this lesson.

  1. Where should you place state that two sibling components need?
    • In the first of the two siblings, which passes it to the other
    • In their closest common ancestor
    • In a global variable declared outside React
    • In each of the two siblings, synchronized by hand
  2. How can a child component modify state owned by its parent?
    • By modifying the received prop directly
    • By calling a callback function passed as a prop by the parent
    • By using useState with the same initial value as the parent
  3. Why avoid storing the same data in two different pieces of state?
    • It uses too much memory
    • React forbids two useState calls with the same initial value
    • The two copies eventually fall out of sync and the interface becomes inconsistent