Capítulo 3 de 116

Chapter 3: Thinking in React

Core Idea

A five-step process for turning a visual mockup (or an existing API/JSON response) into a working React app: break the UI into a component hierarchy, build a static (non-interactive) version first, find the minimal state needed, decide where each piece of state should live, then wire up the inverse data flow.

Key Concepts

  • Step 1 — Break the UI into a component hierarchy: draw boxes around every component and sub-component in the mockup, following the single-responsibility principle — a component should ideally do one thing. This hierarchy usually mirrors your data's own shape (e.g. a JSON API response), which is a useful cross-check.
  • Step 2 — Build a static version first: render the whole hierarchy from props alone, with no state and no interactivity yet. Data flows top-down (props) only. This is deliberately more typing but less thinking — get the rendering right before layering in state.
  • Step 3 — Find the minimal but complete representation of state: for each piece of data, ask three questions — does it stay unchanged over time (not state), is it passed in from a parent via props (not state), can it be computed from existing state/props at render time (not state, it's a derived value). What's left after eliminating all of those is your actual state.
  • Step 4 — Identify where state should live: for each piece of state, find every component that renders based on it, then find their closest common parent component — state goes there. If no clear common owner makes sense, add a new component above them purely to hold it.
  • Step 5 — Add inverse data flow: components below the state owner need to change it — pass event-handler functions down as props (matching the naming convention from Ch 20, e.g. onFilterTextChange) so a child can call back up rather than owning its own copy of that state.

Code Examples

// Step 4 in miniature: state lifted to the closest common parent of
// everything that needs it (a search box and the list it filters)
function FilterableProductTable({ products }) {
  const [filterText, setFilterText] = useState('');
  return (
    <>
      <SearchBar filterText={filterText} onFilterTextChange={setFilterText} />
      <ProductTable products={products} filterText={filterText} />
    </>
  );
}
  • What it demonstrates: Step 4 and Step 5 together — state lives in the common parent, and onFilterTextChange is the inverse-data-flow callback letting the child update it.

Key Takeaways

  1. Steps 1-2 (hierarchy, static render) intentionally involve zero state — resist the urge to add interactivity until the static skeleton is right.
  2. The three-question filter in Step 3 ("unchanging? passed via props? computable from other state?") is the fastest way to avoid the classic beginner bug of redundant state that can drift out of sync (see Ch 28, Avoiding Redundant State).
  3. "Where should this state live" always resolves to "the closest common parent of every component that needs to read it" — never higher than necessary, never split across siblings.

Connects To

  • Ch 28 (Choosing the State Structure): the deeper rules behind Step 3's "what counts as state" filter.
  • Ch 29 (Sharing State Between Components): Step 4's lifting-state-up process in full detail.
  • Ch 15 (Passing Props to a Component): the mechanism Step 5's inverse data flow relies on.