Keeping components pure
Learn why React components should only calculate while rendering, watch a shared variable break a cart, and see how StrictMode's double render catches it.
- 01Tell a pure component from an impure oneCheck that a component only reads its props and returns the same JSX for them, and spot a line that changes something outside it.
- 02Read StrictMode's double renderExplain why React calls your components twice in development, which result it keeps, and what the console shows for it.
- 03Put changes where they belongMove side effects into event handlers or Effects, and know that changing an array or variable you made during the same render is fine.
A numbered cart that counts wrong
A checkout page often numbers the lines in your cart: 1, 2, 3. Here is a first try. Each CartLine adds 1 to a counter and shows the new number. Read it, and guess what the result will show before you look:
let lineNumber = 0; function CartLine({ name }) { lineNumber = lineNumber + 1; return <li>{lineNumber}. {name}</li>;} export default function App() { return ( <ul> <CartLine name="Tea" /> <CartLine name="Cake" /> <CartLine name="Lemonade" /> </ul> );}Line 1 makes a variable, lineNumber, outside every component. Line 4 adds 1 to it each time React calls CartLine, and line 5 shows it next to the name. App uses CartLine three times, on lines 11 to 13. You probably expected 1, 2, 3. The result shows 2. Tea, 4. Cake, and 6. Lemonade.
Nothing is wrong with React. Every live example on this page runs inside <StrictMode>, like a new project does (you met it in Setting up a React project). In development, StrictMode calls each component twice on purpose. Each call of CartLine adds 1 to the same lineNumber, so the numbers jump by two. StrictMode didn't make the bug; it made you see it. The bug is line 4: while React renders CartLine, it changes something that existed before the call.
Here is the cart written so that every call only reads its own inputs:
function CartLine({ number, name }) { return <li>{number}. {name}</li>;} export default function App() { return ( <ul> <CartLine number={1} name="Tea" /> <CartLine number={2} name="Cake" /> <CartLine number={3} name="Lemonade" /> </ul> );}Line 1 takes the number as a prop, beside the name, and line 2 shows it. Lines 8 to 10 give each line its number. There is no variable outside the component any more, so it doesn't matter how many times React calls CartLine: the result is always 1, 2, 3. Components like this one are called pure.
A shop assistant adds up a bill on a calculator, and a doorman clicks a tally counter for every customer who comes in. Ask the assistant to work out the bill again and you get the same total. Click the tally counter again and the count goes up, whether anyone came in or not.
- In real life: A calculator: 3 × 2 shows 6, every time you work it out
- In React: A pure component: the same props give the same JSX
- In real life: A tally counter at a shop door: each press adds 1
- In React: The first CartLine: each call adds 1 to lineNumber
- In real life: Someone presses the tally counter twice by mistake, and the count is wrong
- In React: StrictMode calls CartLine twice, and the numbers jump
- In real life: Working out 3 × 2 twice changes nothing
- In React: Calling a pure component twice changes nothing
A React component should be pure. While React renders it, it only calculates: it reads its props (and later, its state) and returns JSX. It changes nothing that existed before the call, and the same inputs always give the same JSX. React may call it any number of times, so anything else is a bug waiting to show.
What makes a function pure
Purity is a JavaScript idea before it's a React one (the Pure functions lesson covers it in full). A function is pure when two things are true:
- It minds its own business. It changes no variable or object that existed before it was called.
- The same inputs give the same output. Call it with the same arguments, and it returns the same result, every time.
Here are two functions for a cart. One is pure and one isn't. Press Edit & run to see what they print:
function lineTotal(price, quantity) {
return price * quantity;
}
let cartTotal = 0;
function addToCart(price, quantity) {
cartTotal = cartTotal + price * quantity;
return cartTotal;
}
console.log(lineTotal(3, 2));
console.log(lineTotal(3, 2));
console.log(addToCart(3, 2));
console.log(addToCart(3, 2));lineTotal (lines 1 to 3) only multiplies its two arguments, so lines 12 and 13 both print 6. addToCart (lines 7 to 10) also changes cartTotal, a variable made on line 5, outside the function. That's called a side effect: anything a function does besides working out its return value. Because of it, the same call gives 6 on line 14 and 12 on line 15. You can't tell what addToCart(3, 2) returns without knowing what ran before it.
A component is a function too: its inputs are its props, and its output is JSX. React assumes every component is pure, like a formula. This one is:
function CartLine({ name, price, quantity }) { const total = price * quantity; return ( <li> {name} × {quantity}: ${total} </li> );} export default function App() { return ( <ul> <CartLine name="Tea" price={3} quantity={2} /> <CartLine name="Cake" price={4} quantity={1} /> <CartLine name="Tea" price={3} quantity={2} /> </ul> );}Line 1 takes three props. Line 2 makes a new variable, total, from them, and line 5 shows the line. Lines 13 and 15 pass exactly the same props, so they show exactly the same line, Tea × 2: $6. Making total is fine: it's a new variable, made during this call.
CartLine the same props, so they get the same line back, however many times React calls it. Different props, like Cake's on line 14, give a different line.Side effects aren't only changed variables. Each of these makes a component impure if it runs while rendering:
- changing a variable or object from outside the component, including its props;
- changing the page directly, like setting
document.title; - sending a request with fetch, or saving to localStorage;
- reading something that differs from call to call, like
Math.random()or the time from new Date(): it breaks "same inputs, same output".
How StrictMode's double render shows the bug
StrictMode is a component from react that turns on extra checks, in development only, for everything inside it. The most important check: React calls each of your components twice in a row and keeps the second result. A pure component returns the same JSX both times, so you see no difference. An impure one changes something on the first call, and the second call shows it.
A console.log in the component shows both calls. This pure CartLine logs each time React calls it. The console under the result shows what it printed:
function CartLine({ name }) { console.log("Rendering", name); return <li>{name}</li>;} export default function App() { return ( <ul> <CartLine name="Tea" /> <CartLine name="Cake" /> </ul> );}Line 2 logs the name. The console shows Rendering Tea and Rendering Cake, each with a small 2 beside it: when the same line is printed twice in a row, this page's console shows it once, with a count. Both calls printed the same thing, which is what a pure component does. Now the impure one:
let lineNumber = 0; function CartLine({ name }) { lineNumber = lineNumber + 1; console.log(name, "got number", lineNumber); return <li>{lineNumber}. {name}</li>;} export default function App() { return ( <ul> <CartLine name="Tea" /> <CartLine name="Cake" /> </ul> );}Line 5 logs the number each call takes. This time the console shows four different lines: Tea got number 1, Tea got number 2, Cake got number 3, Cake got number 4. Two calls of the same component with the same props disagree: that's the sign of impure rendering. The page shows 2 and 4, the second calls.
This page's previews show both calls' logs in the same way. In your own project, in a browser with the React DevTools extension installed, logs from the second call appear slightly dimmed, so you can tell the two calls apart. React tells DevTools when its second call starts and ends, and DevTools does the dimming. In the DevTools settings, under Debugging, you can turn on "Hide logs during additional invocations in Strict Mode" to hide them. React itself stopped hiding them in React 18.
In a production build, the one npm run build makes for your users, StrictMode does nothing, and React calls each component once. This preview has no production build, but it can leave StrictMode out. Its main.jsx tab starts the gift wrap cart from the last section without it:
import { createRoot } from "react-dom/client";import App from "./App.jsx"; createRoot(document.getElementById("root")).render(<App />);Line 4 renders <App /> with no <StrictMode> around it. The cart shows 1, 2, 3. Press Gift wrap: off and it shows 4, 5, 6: the same bug, found later.
Your component's body isn't the only thing StrictMode repeats. Each row is one thing React does twice in development, and what that finds:
| What | What React does | What it finds |
|---|---|---|
| Your component's body | React calls it twice in a row and keeps the second result. | Rendering that changes something outside the component. |
| Functions you pass to useState, useReducer, useMemo, and to set functions | React calls them twice. | Calculations that aren't pure. |
| Effects | When the component first appears, React runs the Effect, cleans it up, and runs it again. | Effects that don't clean up after themselves. |
| Ref callbacks | When the component first appears, React runs them, cleans up, and runs them again. React 19 added this. | Refs that don't clean up after themselves. |
| Event handlers | React runs them once, as usual. | Nothing: handlers are allowed to change things. |
React 19 changed two details. During the second call of a component, useMemo and useCallback reuse the results from the first call, and ref callbacks now run twice when a component first appears. Effects, refs and useMemo come later in the course. For now, remember: StrictMode never changes what a pure app shows. It only makes impure code show itself sooner. Keep it on.
The second call is in renderWithHooks, the function that calls your component, in ReactFiberHooks.js, line 616, at React 19.3.1. You don't need to read it to use React; How hooks work inside, in the course's last stage, does.
if (shouldDoubleRenderDEV) {
// In development, components are invoked twice to help detect side effects.
setIsStrictModeForDevtools(true);
try {
children = renderWithHooksAgain(
workInProgress,
Component,
props,
secondArg,
);
} finally {
setIsStrictModeForDevtools(false);
}By this point React has called Component, your function, once. Line 1 checks that StrictMode is on in a development build. Line 3 tells DevTools the second call is starting (that's what dims its logs), and lines 5 to 10 call your component again with the same props. Its result replaces children, the first call's result. Line 12 tells DevTools the second call is over.
Where changes belong: event handlers and Effects
Apps have to change things: add an order, save a cart, send a message. Purity doesn't forbid that; it says when. Not while React is rendering. The first place for a side effect is an event handler: a function React runs when something happens, like a click. Handlers are written inside your component, but they don't run while it renders, so they don't need to be pure. (The Introduction to events lesson covers events in plain JavaScript.)
import { useState } from "react"; export default function App() { const [orders, setOrders] = useState([]); function placeOrder(drink) { const number = orders.length + 1; setOrders([...orders, `#${number} ${drink}`]); console.log("Order placed:", number); } return ( <> <button onClick={() => placeOrder("Tea")}>Order tea</button> <button onClick={() => placeOrder("Cake")}>Order cake</button> <ul> {orders.map((order) => ( <li key={order}>{order}</li> ))} </ul> </> );}Line 4 keeps the orders in state. placeOrder (lines 6 to 10) is the click handler: line 7 works out the next number, line 8 gives React a new list with the order added, and line 9 logs it. Click Order tea, then Order cake: the list shows #1 Tea and #2 Cake, and the console shows Order placed: 1 and Order placed: 2, once each. StrictMode doesn't call event handlers twice. The numbers come from orders, the component's state, not from a variable outside it, and React renders the list from that state.
Some side effects aren't caused by any event. Say the browser tab's title should show how many items are in the cart, whatever changed the count. For that, React has Effects: code that runs after React has put a render on the page. Use them as a last resort, when no event handler fits.
import { useEffect, useState } from "react"; export default function App() { const [count, setCount] = useState(0); useEffect(() => { document.title = `Cart (${count})`; console.log("Saved count:", count); }); return <button onClick={() => setCount(count + 1)}>Items: {count}</button>;}Lines 6 to 9 pass a function to useEffect. React runs it after each render reaches the page: line 7 sets the tab's title, and line 8 logs. When the result first appears, the console shows Saved count: 0 twice. That's StrictMode's check for Effects: when a component first appears, React runs its Effect, cleans it up, and runs it again, in development only. Click the button and you get one Saved count: 1 per click. The Synchronizing with effects lesson teaches Effects properly, including the clean up.
How to read the table: each row is a place code can sit in a component, when it runs, and whether it may change things.
| Where the code is | When it runs | Can it change things? |
|---|---|---|
| The component's body (rendering) | Every time React renders the component, and twice in development with StrictMode. | No. It only works out what to show. |
| An event handler, like onClick | Once each time the event happens, like a click. | Yes. This is the first place to put side effects. |
| An Effect, from useEffect | After React has put a render on the page. | Yes, as a last resort, to keep something outside React in step. |
Why does React care so much? Because purity lets React do its job well:
- It can skip a component whose props didn't change and reuse the last result. The memo, useMemo and useCallback and The React Compiler lessons show how.
- It can start a render, stop, and start again when newer data arrives, without anything half-changed. Transitions rely on this.
- It can render the same component on a server for many visitors. Server rendering and hydration covers it.
Changing what you just made is fine
Purity is about things that existed before the call. A component can make its own arrays and variables while it renders, and change them as much as it likes. That's called local mutation (mutation means changing a value in place). Nobody outside the call can see it:
const items = [ { name: "Tea", price: 3 }, { name: "Cake", price: 4 }, { name: "Lemonade", price: 2 },]; export default function Receipt() { const lines = []; let total = 0; for (const item of items) { total = total + item.price; lines.push( <li key={item.name}> {item.name}: ${item.price} (so far ${total}) </li> ); } return <ul>{lines}</ul>;}Line 8 makes a new, empty array, and line 9 a new total of 0. The loop on lines 10 to 17 changes both: line 11 adds to total, and line 12 pushes a line into lines. Line 18 returns them. The result shows three lines, ending Lemonade: $2 (so far $9). StrictMode called Receipt twice, and each call started again from an empty array and 0, so both calls returned the same receipt. Compare that with the numbered cart: there, lineNumber was made once, on line 1, outside every call.
The thing to watch is an array or object you were given. Props are shared: the parent, and maybe other components, hold the very same array (the Objects and references lesson explains why). Some array methods change the array itself. sort is one:
const menu = [ { name: "Tea", price: 3 }, { name: "Cake", price: 4 }, { name: "Lemonade", price: 2 },]; function CheapestFirst({ items }) { items.sort((a, b) => a.price - b.price); return <p>Cheapest first: {items.map((item) => item.name).join(", ")}</p>;} function Menu({ items }) { return <p>Menu: {items.map((item) => item.name).join(", ")}</p>;} export default function App() { return ( <> <Menu items={menu} /> <CheapestFirst items={menu} /> <Menu items={menu} /> </> );}Lines 1 to 5 make one menu, and App passes it to three components on lines 19 to 21. Line 8 sorts items, cheapest first. But items is menu: the same array. The first Menu rendered before the sort and shows Tea, Cake, Lemonade. The second rendered after it and shows Lemonade, Tea, Cake. Same component, same props, different result: the order of rendering decided it.
items.sort() sorts the shared menu itself, so the second Menu shows the new order. Right: [...items] makes a new array during this call, and sorting it changes nothing anyone else can see.The fix is to sort a copy that you make during this call:
const menu = [ { name: "Tea", price: 3 }, { name: "Cake", price: 4 }, { name: "Lemonade", price: 2 },]; function CheapestFirst({ items }) { const sorted = [...items].sort((a, b) => a.price - b.price); return <p>Cheapest first: {sorted.map((item) => item.name).join(", ")}</p>;} function Menu({ items }) { return <p>Menu: {items.map((item) => item.name).join(", ")}</p>;} export default function App() { return ( <> <Menu items={menu} /> <CheapestFirst items={menu} /> <Menu items={menu} /> </> );}Line 8 makes a new array with spread, [...items], and sorts that. Sorting the copy is local mutation, so it's fine. Line 9 shows the copy. Both Menu lines now show Tea, Cake, Lemonade. Arrays also have toSorted(), which returns a sorted copy in one step: items.toSorted((a, b) => a.price - b.price). The Sorting arrays and Immutability lessons list which methods change the array and which make a copy.
A checkout summary that stays pure
Here is everything together in a checkout page: lines sorted by cost, a subtotal, a discount, and a button that places orders. Click Place order twice:
import { useState } from "react"; const cart = [ { name: "Cake", price: 4, quantity: 1 }, { name: "Tea", price: 3, quantity: 2 }, { name: "Lemonade", price: 2, quantity: 4 },]; function Summary({ items, discount }) { const sorted = [...items].sort((a, b) => b.price * b.quantity - a.price * a.quantity); let subtotal = 0; for (const item of sorted) { subtotal = subtotal + item.price * item.quantity; } const total = subtotal - discount; return ( <> <ol> {sorted.map((item) => ( <li key={item.name}> {item.name} × {item.quantity}: ${item.price * item.quantity} </li> ))} </ol> <p>Total: ${total}</p> </> );} export default function App() { const [orders, setOrders] = useState([]); function placeOrder() { setOrders([...orders, orders.length + 1]); } return ( <> <Summary items={cart} discount={2} /> <button onClick={placeOrder}>Place order</button> <p>Orders placed: {orders.length ? orders.map((n) => `#${n}`).join(", ") : "none"}</p> </> );}Summary (lines 9 to 28) only calculates. Line 10 sorts a copy of items, most expensive line first. Lines 11 to 14 add up a subtotal in a local variable, and line 15 takes off the discount. The result lists Lemonade × 4: $8, Tea × 2: $6, and Cake × 1: $4, then Total: $16. The one change to the app's data happens in placeOrder (lines 33 to 35), a click handler, which gives React a new list of orders. After two clicks the page says Orders placed: #1, #2. Render it as often as you like: the summary stays the same, and only clicks add orders.
lineNumber = lineNumber + 1;const total = price * quantity;items.sort(byPrice);const sorted = [...items].sort(byPrice);document.title = `Cart (${count})`;lines.push(<li key={name}>{name}</li>);const label = name.toUpperCase();fetch("/api/orders", { method: "POST" });
Each line sits in a component's body, so it runs while React renders. There, items comes from the props, lines was made earlier in the same render, and lineNumber lives outside the component. Is the line fine there, or a side effect that belongs somewhere else?
Common mix-ups
- "StrictMode causes the bug." No. The bug is the impure code. StrictMode makes it show on the first render instead of a later one. Without it, the gift wrap cart went wrong on the first click.
- "My users see everything twice." No. The double call happens only in development. A production build calls each component once.
- "A pure component can't change any variable." No. It can make and change its own variables and arrays during the render, like
totalandlinesin the receipt. - "Event handlers must be pure too." No. They run when something happens, not while rendering. They are the first place for side effects.
- "Sorting a prop is fine, it's only reading it." No.
sort,reverse, andpushchange the array itself, and props are shared with the parent. Sort a copy. - "Pure components render less often." No. Purity doesn't change when React renders. It makes rendering safe to repeat, skip, or throw away, which is what React's performance tools rely on.
Practice exercises
Every result here runs in StrictMode, like the examples above. Make a guess first, then use the hints. Press Edit & run to change the code and watch the result.
What does the heading say in development, with StrictMode? Answer first, then press Edit & run to check.
let visits = 10;
export default function Welcome() {
visits = visits + 1;
return <h1>Welcome back! Visit {visits}</h1>;
}let visits = 10; export default function Welcome() { visits = visits + 1; return <h1>Welcome back! Visit {visits}</h1>;}StrictMode calls Welcome twice. Line 4 runs both times, so visits goes from 10 to 11 to 12, and React keeps the second call: Welcome back! Visit 12. A production build would show Visit 11, until the next render.
The heading looks right. Which line makes this component impure?
let lastShown = ""; export default function ProductTitle({ name = "Tea", price = 3 }) { const label = name.toUpperCase(); const withPrice = `${label} ($${price})`; lastShown = withPrice; return <h2>{withPrice}</h2>;}export default function ProductTitle({ name = "Tea", price = 3 }) { const label = name.toUpperCase(); const withPrice = `${label} ($${price})`; return <h2>{withPrice}</h2>;}Line 6 writes to lastShown, which exists before the call: a side effect. Lines 4 and 5 only make new values from the props. Delete line 6 (and the variable) and the component shows the same TEA ($3), now as a pure component.
The cart shows 2, 4, 6. Fix it so the numbers come from each name's place in cart, not from a shared counter. What does the third line show?
const cart = ["Tea", "Cake", "Lemonade"];let count = 0; function CartLine({ name }) { count = count + 1; return <li>{count}. {name}</li>;} export default function App() { return ( <ul> {cart.map((name) => ( <CartLine key={name} name={name} /> ))} </ul> );}const cart = ["Tea", "Cake", "Lemonade"]; function CartLine({ number, name }) { return <li>{number}. {name}</li>;} export default function App() { return ( <ul> {cart.map((name, index) => ( <CartLine key={name} number={index + 1} name={name} /> ))} </ul> );}Each line now gets its number as a prop, index + 1, worked out from its place in the list. CartLine only reads its props, so StrictMode's second call gives the same line, and the cart shows 1. Tea, 2. Cake, 3. Lemonade.
The second Menu shows the cheapest-first order. Fix CheapestFirst so it doesn't change the menu. What does the second Menu line show then?
const menu = [ { name: "Tea", price: 3 }, { name: "Cake", price: 4 }, { name: "Lemonade", price: 2 },]; function CheapestFirst({ items }) { items.sort((a, b) => a.price - b.price); return <p>Cheapest first: {items.map((item) => item.name).join(", ")}</p>;} function Menu({ items }) { return <p>Menu: {items.map((item) => item.name).join(", ")}</p>;} export default function App() { return ( <> <Menu items={menu} /> <CheapestFirst items={menu} /> <Menu items={menu} /> </> );}const menu = [ { name: "Tea", price: 3 }, { name: "Cake", price: 4 }, { name: "Lemonade", price: 2 },]; function CheapestFirst({ items }) { const sorted = [...items].sort((a, b) => a.price - b.price); return <p>Cheapest first: {sorted.map((item) => item.name).join(", ")}</p>;} function Menu({ items }) { return <p>Menu: {items.map((item) => item.name).join(", ")}</p>;} export default function App() { return ( <> <Menu items={menu} /> <CheapestFirst items={menu} /> <Menu items={menu} /> </> );}[...items] makes a new array during this call, so sorting it is local mutation. menu keeps its order, and both Menu lines show Menu: Tea, Cake, Lemonade. items.toSorted(…) would work too.
Click Place order twice: the tickets read 2, 4. Move the numbering out of rendering, into the click. What does the page say after two clicks?
import { useState } from "react"; let nextTicket = 1; export default function App() { const [orders, setOrders] = useState([]); const ticket = nextTicket; nextTicket = nextTicket + 1; return ( <> <button onClick={() => setOrders([...orders, ticket])}>Place order</button> <p>Tickets: {orders.join(", ")}</p> </> );}import { useState } from "react"; export default function App() { const [orders, setOrders] = useState([]); function placeOrder() { setOrders([...orders, orders.length + 1]); } return ( <> <button onClick={placeOrder}>Place order</button> <p>Tickets: {orders.join(", ")}</p> </> );}The starter took a ticket number on every render, and StrictMode renders twice, so the clicks got 2 and 4. Now the number is worked out in placeOrder, a click handler, from the orders in state. Rendering only shows the list, so the page says Tickets: 1, 2.
The receipt is written by hand and shows only Tea. Build one line per item with a loop, a local array, and a running total. What does the last line show?
const items = [ { name: "Tea", price: 3 }, { name: "Cake", price: 4 }, { name: "Lemonade", price: 2 },]; export default function Receipt() { return ( <ul> <li>Tea: $3 (total $3)</li> </ul> );}const items = [ { name: "Tea", price: 3 }, { name: "Cake", price: 4 }, { name: "Lemonade", price: 2 },]; export default function Receipt() { const lines = []; let total = 0; for (const item of items) { total = total + item.price; lines.push( <li key={item.name}> {item.name}: ${item.price} (total ${total}) </li> ); } return <ul>{lines}</ul>;}lines and total are made fresh on every call, so changing them is local mutation, and StrictMode's second call builds the same receipt. The last line is Lemonade: $2 (total $9): 3 + 4 + 2.
Check your understanding
For each code question, ask: does anything the component changes exist before the call? And remember that StrictMode calls it twice. Press Edit & run on a question's code to check your answer.
Question 1 of 7What makes a React component pure?
Choose an answer to see the explanation.
Question 2 of 7With StrictMode on, in development, what does this guest list show?
Read the code, then predictlet seat = 0; function Guest({ name }) { seat = seat + 1; return <li>Seat {seat}: {name}</li>; } export default function App() { return ( <ul> <Guest name="Asha" /> <Guest name="Ben" /> </ul> ); }Choose an answer to see the explanation.
Question 3 of 7A production build shows the guest list as Seat 1: Asha, Seat 2: Ben. What happens when the page renders again, for example after a state change in a parent?
Choose an answer to see the explanation.
Question 4 of 7Is this component pure?
Read the code, then predictexport default function Basket({ names = ["Tea", "Cake"] }) { const items = []; for (const name of names) { items.push(<li key={name}>{name}</li>); } return <ul>{items}</ul>; }Choose an answer to see the explanation.
Question 5 of 7What does TopSeller show?
Read the code, then predictconst drinks = ["Tea", "Coffee", "Lemonade"]; function AToZ({ items }) { return <p>A to Z: {items.sort().join(", ")}</p>; } function TopSeller({ items }) { return <p>Top seller: {items[0]}</p>; } export default function App() { return ( <> <AToZ items={drinks} /> <TopSeller items={drinks} /> </> ); }Choose an answer to see the explanation.
Question 6 of 7You want a Place order button to send the cart to the server. Where does the
fetchcall go?Choose an answer to see the explanation.
Question 7 of 7You have React DevTools installed and StrictMode on. How do the logs from a component's second call look in your browser's console?
Choose an answer to see the explanation.
Key takeaways
- A pure component changes nothing that existed before the call, and returns the same JSX for the same props.
- Changing a variable from outside while rendering makes the result depend on how often, and in what order, React rendered: the numbered cart climbed on every render.
- In development, StrictMode calls each component twice in a row and keeps the second result, so impure rendering shows at once. A production build calls it once.
- This page's console prints both calls; with React DevTools installed, the second call's logs are dimmed.
- Side effects go in event handlers first, and in Effects, which run after the render reaches the page, as a last resort.
- Changing arrays and variables you made during the same render is fine. Copy a prop before you sort it.
Remember the one-liner.
Rendering only calculates; changes happen in handlers and Effects.
Coming next: Your UI as a tree. It shows how React sees your components as a tree, and why that tree explains rendering, state, and performance.