Thinking in React
A repeatable process for turning a UI design or mockup into a component tree and a state structure.
2 min read
Going from "here's a design" to "here's the React code" is a skill on its own, separate from knowing the syntax. React's own docs popularized a five-step process for it, and it still holds up as a mental model.
1. Break the UI into a component hierarchy
Look at the mockup and draw boxes around every distinct piece — the same way you'd identify layers in a design file. A product page might break into SearchBar, ProductTable, and within the table, ProductCategoryRow and ProductRow. Nesting in the design usually maps directly to nesting in the component tree.
2. Build a static version first
Build the whole tree using only props, with no state at all — every component just renders what it's handed:
function ProductRow({ product }) {
return (
<tr>
<td>{product.name}</td>
<td>{product.price}</td>
</tr>
);
}This gets the rendering and composition correct before you introduce anything that changes over time, which is deliberately the harder problem to debug if mixed in from the start.
3. Find the minimal set of state
Now figure out what actually needs to be interactive — a search filter, a checkbox for "only show in stock." For each piece of data, ask: does it change over time? Can it be computed from props or other state instead? If a value can be derived from something you already have, it isn't state — it's a calculation you do during render. Only what's left after that filtering is real state.
4. Identify where state should live
For each piece of state, find the highest component in the tree that needs it. If only one component uses it, it can live there locally with useState. If multiple sibling components need to read or react to the same value, it has to live in their closest common ancestor and get passed down as props — a pattern called lifting state up, covered in the next lesson.
5. Add inverse data flow
Props only flow down, so a child that needs to change a parent's state (a search box updating a filter, for example) does it by calling a function the parent passed down as a prop:
function SearchBar({ query, onQueryChange }) {
return (
<input value={query} onChange={(e) => onQueryChange(e.target.value)} />
);
}SearchBar never touches state directly — it just calls onQueryChange, and the parent decides what that means.
This process front-loads the hard design decisions (what's a component, what's state, where does it live) before you write any state management code — which is exactly what the next two lessons dig into.