Destructuring syntax for props requires double curly braces
When declaring component props, you must use the correct destructuring syntax with curly braces inside the function parentheses: function Avatar({ person, size }) { }. The destructuring syntax is equivalent to reading properties from the props object manually.
How to specify default values for props
You can provide a default value for a prop by using the equals sign in the destructuring syntax: function Avatar({ person, size = 100 }) { }. The default value is used when the prop is missing or when size={undefined}. However, if you pass size={null} or size={0}, the default value will not be used.
JSX spread syntax for forwarding props
You can forward all props to a child component using the spread syntax: function Profile(props) { return <Avatar {...props} />; }. This passes all of Profile's props to Avatar without listing each name individually. Use this sparingly to avoid making code harder to follow.
When to avoid spread syntax for forwarding props
Do not use spread syntax indiscriminately. If you find yourself using it in many components, it often indicates you should split your components differently or pass children as JSX instead. Spread syntax should be used with restraint.
How to pass JSX as children via the children prop
When you nest content inside a JSX tag like <Card><Avatar /></Card>, the parent component receives that nested content in a special prop called 'children'. Inside the Card component, you can render it with {children}. This allows parent components to pass arbitrary JSX into the component.
Children prop enables flexible wrapper components
Components with a children prop act like containers with a 'hole' that can be filled by parent components with arbitrary JSX. This pattern is commonly used for visual wrappers like panels, grids, cards, and dialogs that don't need to know what content they're wrapping.
React Compiler eliminates need for manual memoization
React Compiler automatically optimizes applications by applying memoization without requiring manual use of useMemo, useCallback, or React.memo. This frees developers from manually memoizing components and values to keep apps responsive.
React Compiler focuses on two optimization use cases
React Compiler automatically optimizes for two primary use cases: skipping cascading re-rendering of components in the component tree, and skipping expensive calculations from outside of React, such as processing large arrays of objects inside components or hooks.
useMemo and useCallback can still be used with React Compiler as escape hatches
useMemo and useCallback hooks can continue to be used with React Compiler as escape hatches to provide explicit control over which values are memoized. A common use-case is when a memoized value is used as an effect dependency to ensure an effect does not fire repeatedly.
React Compiler automatically skips unnecessary re-renders
React Compiler automatically applies fine-grained reactivity by analyzing dependencies and memoizing component return values. This ensures that only relevant parts of an app re-render as state changes, and child components like MessageButton are not re-rendered when a parent's state changes if they are not affected.
React Compiler memoizes expensive calculations inside components and hooks
React Compiler automatically memoizes expensive calculations used during rendering within components and hooks. However, it does not memoize standalone functions or calculations shared across multiple components, and its memoization is not shared across different components or hooks.
Example code before React Compiler requires manual memoization
Before React Compiler, manual memoization was required using memo, useMemo, and useCallback. Even with these, arrow functions in JSX like `() => handleClick(item)` create new function instances every render, breaking memoization. Here is an example: import { useMemo, useCallback, memo } from 'react'; const ExpensiveComponent = memo(function ExpensiveComponent({ data, onClick }) { const processedData = useMemo(() => { return expensiveProcessing(data); }, [data]); const handleClick = useCallback((item) => { onClick(item.id); }, [onClick]); return ( <div> {processedData.map(item => ( <Item key={item.id} onClick={() => handleClick(item)} /> ))} </div> ); });
Example code with React Compiler requires no manual memoization
With React Compiler, the same component can be written without manual memoization and the compiler automatically handles optimization correctly: function ExpensiveComponent({ data, onClick }) { const processedData = expensiveProcessing(data); const handleClick = (item) => { onClick(item.id); }; return ( <div> {processedData.map(item => ( <Item key={item.id} onClick={() => handleClick(item)} /> ))} </div> ); }
Pitfall: arrow functions in onClick props break manual memoization
Creating arrow functions directly in JSX props like `onClick={() => handleClick(item)}` creates a new function every render, even if handleClick is wrapped in useCallback. This breaks memoization because the Item component receives a new onClick prop each time. React Compiler handles this correctly automatically.
Recommendation for memoization with React Compiler
For new code, rely on React Compiler for memoization and use useMemo/useCallback only where needed to achieve precise control. For existing code, either leave existing memoization in place or carefully test before removing it, as removing memoization can change compilation output.
Declarative vs imperative UI programming
In imperative programming, you write exact instructions to manipulate the UI in response to events (e.g., enable/disable buttons, show/hide elements). In declarative programming with React, you describe what the UI should look like in each state, and React figures out how to update it. Declarative UI is easier to maintain as complexity grows because you describe the desired result rather than manually orchestrating every DOM change.
Flight protocol supports Map, Set, Date, and BigInt
The React Flight protocol (used for server components and server actions) can serialize and deserialize Map, Set, Date, and BigInt objects across the server-client boundary. These types survive round-trip transmission from server components to client components and vice versa.
Basic server component example
A server component is defined as a regular component without the 'use client' directive. It renders on the server and sends HTML to the client. Example: export default function App() { return <h1>Hello from a Server Component!</h1>; }
Mark component as client with 'use client' directive
Add the string 'use client' at the top of a file to mark all exported components in that file as client components. Client components can use hooks like useState and handle interactivity. Example: 'use client'; import { useState } from 'react'; export default function Counter() { const [count, setCount] = useState(0); return <button>Count: {count}</button>; }
Server components can import client components
A server component can import and render client components as children or nested elements. The server component renders on the server while the client component renders on the client with interactive capabilities.
Streaming incremental rendering with Suspense
Streaming allows the server component shell to render instantly with Suspense fallbacks, while async components stream in later and replace their fallback without re-rendering outer content. This demonstrates incremental rendering where different parts of the page become interactive at different times.
Server actions marked with 'use server' directive
Define server-side functions that can be called from client components by marking them with 'use server' at the top of the function or file. Server actions receive data from the client, execute on the server, and can mutate server-side state. Example: 'use server'; let count = 0; export async function addLike() { count++; }
Flight encodeReply and decodeReply for server actions
When calling server actions from client components, the Flight protocol automatically encodes and decodes arguments and return values using encodeReply and decodeReply. This preserves typed data like Map, Set, Date, and BigInt across the client-server boundary.
Server action mutation triggers full server component re-render
After a server action completes and mutates server-side data, the framework automatically re-renders the entire server component tree. This causes server components to re-read the updated data and stream the new UI to the client, updating the view without explicit client state management.
Inline server actions inside server components
Server actions can be defined inline within a server component using 'use server' on the function body, without a separate actions file. The action closes over module-level state and can be passed as a prop to client components. Example: let count = 0; export default function App() { async function addLike() { 'use server'; count++; } return <LikeButton addLike={addLike} />; }
Top-down vs bottom-up component building
When building the static version of a React app, you can build top-down by starting with components higher up in the hierarchy (like root components) or bottom-up by starting with components lower down (like leaf components). For simpler examples, top-down is usually easier. For larger projects, bottom-up is often easier.
Build static version first before adding interactivity
When implementing a React app, build a static version that renders the UI from the data model without interactivity first. This approach requires a lot of typing and no thinking, but adding interactivity later requires a lot of thinking and not a lot of typing. Use props to pass data from parent to child components in the static version, and do not use state at all since state is reserved only for data that changes over time.
Breaking UI into a component hierarchy strategy
When breaking a UI into components, consider three approaches: Programming - use separation of concerns technique so each component ideally concerns itself with one thing and decomposes into smaller subcomponents if it grows; CSS - consider what you would make class selectors for; Design - consider how you would organize the design's layers. If the JSON data structure is well-organized, it will naturally map to the component structure because UI and data models often share the same information architecture.
Five-step process for building React applications
Building a user interface with React follows five key steps: Step 1 - Break the UI into a component hierarchy by drawing boxes around components and subcomponents in the mockup and naming them. Step 2 - Build a static version in React that renders the UI from the data model without adding interactivity. Step 3 - Find the minimal but complete representation of UI state by identifying which data pieces are state. Step 4 - Identify where the state should live by finding the closest common parent component. Step 5 - Add inverse data flow by passing callback functions down from parent to child components to update state based on user input.
Static component example - FilterableProductTable
Here is a complete static version of a searchable product table without interactivity:
```jsx
import { useState } from 'react';
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>
);
}
function SearchBar() {
return (
<form>
<input type="text" placeholder="Search..." />
<label>
<input type="checkbox" />
{' '}
Only show products in stock
</label>
</form>
);
}
function FilterableProductTable({ products }) {
return (
<div>
<SearchBar />
<ProductTable products={products} />
</div>
);
}
```
This example shows how to build the initial static version using props to pass data down the hierarchy without any state or interactivity.
Searchable product table component hierarchy example
A searchable product table component structure has five components: FilterableProductTable (root component containing the entire app), SearchBar (receives user input), ProductTable (displays and filters the product list), ProductCategoryRow (displays a heading for each category), and ProductRow (displays a row for each product). The hierarchy is: FilterableProductTable contains SearchBar and ProductTable as children; ProductTable contains ProductCategoryRow and ProductRow as children.
One-way data flow in React
React uses one-way data flow where data flows down from the top-level component to components at the bottom of the tree. The component at the top of the hierarchy takes the data model as a prop and passes data down through the component tree to child components.
Data structure example for product list
Product data should be structured as an array of objects with the following properties: category (string), price (string), stocked (boolean), and name (string). Example data: [{category: "Fruits", price: "$1", stocked: true, name: "Apple"}, {category: "Fruits", price: "$2", stocked: false, name: "Passionfruit"}, {category: "Vegetables", price: "$2", stocked: true, name: "Spinach"}]
React.CSSProperties for inline style prop typing
React.CSSProperties is a type union of all possible CSS properties. Use it to type the style prop on components to ensure valid CSS properties are passed and to get autocomplete in the editor.
React.ReactElement type for children prop
React.ReactElement is a more restrictive type that only includes JSX elements and excludes JavaScript primitives like strings or numbers. Use this when children must be JSX elements.
React.ReactNode type for children prop
React.ReactNode is a union of all possible types that can be passed as children in JSX, including strings, numbers, elements, and fragments. Use this for a broad definition of children.
Top-level components in render tree
Top-level components are components nearest to the root component in the render tree. They affect the rendering performance of all components beneath them and often contain the most complexity. Identifying them is useful for understanding data flow and app performance.
Render tree definition and purpose
A render tree is a UI tree composed of React components nested within each other. It represents a single render pass of a React application and models the parent-child relationships between components. The root node is the root component that React renders first, and arrows point from parent components to child components.
Render tree does not include HTML tags
The render tree only contains React components, not HTML tags or other platform-specific UI primitives. This is because React is platform agnostic and can render to web (HTML), mobile (UIView), or desktop platforms (FrameworkElement). The render tree provides insight regardless of the target platform.
Conditional rendering affects the render tree
A parent component may render different children depending on the data passed to it. With conditional rendering, the render tree may differ across different render passes. This is useful for understanding how components may change between renders.
Leaf components in render tree
Leaf components are near the bottom of the render tree and have no child components. They are often frequently re-rendered. Identifying them is useful for understanding data flow and debugging rendering performance.
Props pass data from parent to child components
Props (properties) allow you to pass data from parent components to child components. A parent component can pass values to a child component using prop syntax like <Square value="1" />. The child component receives these props as function parameters, e.g., function Square({ value }).
Example: rendering list of history buttons with map
const moves = history.map((squares, move) => {
let description;
if (move > 0) {
description = 'Go to move #' + move;
} else {
description = 'Go to game start';
}
return (
<li key={move}>
<button onClick={() => jumpTo(move)}>{description}</button>
</li>
);
});
This example shows how to transform a history array into a list of buttons using map, with each button having a key prop set to the move index.
Example: using move index as key in tic-tac-toe game
In the tic-tac-toe game example, each past move has a unique ID: the sequential number of the move. Since moves will never be re-ordered, deleted, or inserted in the middle, it is safe to use the move index as a key. The implementation uses `<li key={move}>` where move is the array index from the history.map() callback.
Default key behavior when key is not specified
If no key is specified on list items, React will report an error and use the array index as a key by default.
Pitfall: using array index as key when list can be reordered
Using the array index as a key is problematic when trying to re-order a list's items or when inserting/removing list items. Explicitly passing key={i} silences the React key error but has the same problems as array indices and is not recommended in most cases. It's strongly recommended to assign proper keys whenever building dynamic lists, and if you don't have an appropriate key, you may want to consider restructuring your data.
Key is a special reserved property in React
The key prop is special and reserved in React. When an element is created, React extracts the key property and stores the key directly on the returned element. Even though key may look like it is passed as props, React automatically uses key to decide which components to update. There is no way for a component to ask what key its parent specified.
How React uses key to identify components
When a list is re-rendered, React takes each list item's key and searches the previous list's items for a matching key. If the current list has a key that didn't exist before, React creates a new component. If the current list is missing a key that existed in the previous list, React destroys the previous component. If two keys match, React moves the corresponding component. If a component's key changes, the component will be destroyed and re-created with a new state.
Key uniqueness requirement and scope
Keys do not need to be globally unique across an entire application. They only need to be unique between a component and its siblings in the same list.
Purpose of key prop in list rendering
When you render a list, React stores information about each rendered list item. When you update a list, React needs to determine what has changed. The key property tells React about the identity of each component, which allows React to maintain state between re-renders. Keys help React understand whether items have been added, removed, re-arranged, or updated.
Key prop missing from list items causes React warning
When rendering a list of items in React without a key prop, React displays a console warning: 'Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of [ComponentName].'
Map over history array with index to create list buttons
When using map to iterate through a history array, the map callback receives two parameters: the array element (squares) and its index (move). The move index parameter goes through each array index: 0, 1, 2, etc. This pattern is useful when you only need the indexes rather than the actual array elements.
Rendering arrays of React elements in lists
React elements like <button> are regular JavaScript objects that can be passed around in applications. To render multiple items in React, you can use an array of React elements. The array.map() method transforms an array into another array of React elements.
React Developer Tools extension for browser debugging
React Developer Tools is available for Chrome, Firefox, and Edge browsers. After installation, a Components tab appears in browser DevTools for React sites. Use the inspect button in the top-left of the Components tab to select and view a component's props and state.
CSS className prop styles React components
React uses the className prop (not class) to apply CSS classes to elements. For example, <button className="square"> applies the square CSS class defined in styles.css.
Components with capital letters are custom React components
React components must start with capital letters (e.g., Square, Board, Game). Lowercase names like <div> are treated as built-in HTML elements. This convention helps React distinguish between custom components and HTML elements.
export default makes function the main export of a module
The export default keywords in React make a function the default export of its file. Only one default export is allowed per file. When importing, you receive this default export. Removing export default from one function and adding it to another changes which component is the main export.
JavaScript closures enable inner functions to access outer scope
JavaScript closures allow inner functions to access variables and functions defined in outer scopes. Event handlers like handleClick can access the squares state and setSquares function from the parent Board component scope.
calculateWinner function checks three-in-a-row
function calculateWinner(squares) {
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];
for (let i = 0; i < lines.length; i++) {
const [a, b, c] = lines[i];
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
return squares[a];
}
}
return null;
}
This function checks all eight winning combinations (three rows, three columns, two diagonals). Returns 'X', 'O', or null.
JSX curly braces escape into JavaScript
In JSX, curly braces {} allow you to embed JavaScript expressions. Without curly braces, text inside JSX elements is treated as literal strings. For example, {value} renders the JavaScript variable, while value renders the word 'value'.