Appearance
Thinking in React
FreshReact changes the way you think about UI design and building. Here is the mental model for building any UI with React.
The 5-Step Process
flowchart TD
A[1. Break UI into component hierarchy] --> B[2. Build a static version]
B --> C[3. Find the minimal state]
C --> D[4. Identify where state lives]
D --> E[5. Add inverse data flow]Step 1: Break the UI into a Component Hierarchy
Draw boxes around every component and subcomponent in the mockup and name them. Use the single responsibility principle: a component should ideally only do one thing.
Step 2: Build a Static Version in React
Build a version that renders the UI from your data model without adding any interactivity. Don't use state at all for the static version; state is reserved for interactivity.
jsx
function ProductCategoryRow({ category }) {
return (
<tr>
<th colSpan="2">{category}</th>
</tr>
);
}
function ProductRow({ product }) {
const name = product.stocked ? product.name :
<span style={{ color: 'red' }}>{product.name}</span>;
return (
<tr>
<td>{name}</td>
<td>{product.price}</td>
</tr>
);
}
function ProductTable({ products }) {
const rows = [];
let lastCategory = null;
products.forEach((product) => {
if (product.category !== lastCategory) {
rows.push(
<ProductCategoryRow
category={product.category}
key={product.category}
/>
);
}
rows.push(
<ProductRow product={product} key={product.name} />
);
lastCategory = product.category;
});
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
}Step 3: Find the Minimal but Complete Representation of UI State
Think of state as the minimal set of changing data that your app needs to remember.
The key principle: Don't Repeat Yourself (DRY). Figure out the absolute minimum representation of the state your application needs and compute everything else on demand.
Ask three questions about each piece of data:
- Does it remain unchanged over time? If so, it isn't state.
- Is it passed in from a parent via props? If so, it isn't state.
- Can you compute it based on existing state or props? If so, it definitely isn't state.
Step 4: Identify Where Your State Should Live
For each piece of state:
- Identify every component that renders something based on that state
- Find their closest common parent component
- The common parent (or a component above it) should own the state
Step 5: Add Inverse Data Flow
Pass callback functions from parent to child so that children can update the parent's state:
jsx
function FilterableProductTable({ products }) {
const [filterText, setFilterText] = useState('');
const [inStockOnly, setInStockOnly] = useState(false);
return (
<div>
<SearchBar
filterText={filterText}
inStockOnly={inStockOnly}
onFilterTextChange={setFilterText}
onInStockOnlyChange={setInStockOnly}
/>
<ProductTable
products={products}
filterText={filterText}
inStockOnly={inStockOnly}
/>
</div>
);
}Key Takeaway
Data flows down through props. Events flow up through callbacks. State lives in the closest common ancestor of the components that need it.