Place each piece of state in the closest common ancestor to share data between several components without duplicating it.
Open this lesson in KodokonTwo 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.
import { useState } from "react";
function ProductPage({ products }) {
const [query, setQuery] = useState("");
return (
<>
<SearchBar query={query} onChange={setQuery} />
<ProductList products={products} query={query} />
</>
);
}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.
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>
);
}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.