Ten questions across the chapter, each built on a React 7,897 behavior that surprises people the first time it bites them. Work out what each snippet renders or logs before you read on. The answers, with the reasoning and the section each comes from, are in Appendix G.
Questions
// 1. `items` is an empty array. What exact HTML does renderToStaticMarkup produce?
const App = () => (
<div>
{items.length && <p>has items</p>}
<span>{false}{null}{undefined}{true}{0}{NaN}</span>
</div>
);
// 2. The user clicks twice, a second apart. What is in `log` afterwards, what does the
// button show, and what one edit makes it count up by two per click?
function Counter() {
const [n, setN] = useState(0);
useEffect(() => { log.push('effect ' + n); }, [n]);
const bump = () => { setN(n + 1); setN(n + 1); log.push('handler ' + n); };
return <button onClick={bump}>{n}</button>;
}
// 3. A development build, mounted once inside <StrictMode>. What are the four counts,
// and which of them change in a production build?
const c = { render: 0, init: 0, setup: 0, cleanup: 0 };
function Probe() {
c.render++;
const [v] = useState(() => { c.init++; return 1; });
useEffect(() => { c.setup++; return () => { c.cleanup++; }; }, []);
return <p>{v}</p>;
}// 4. What is on each button after mount, and after one click on each — and what is
// `calls` at every step?
let calls = 0;
const expensive = () => { calls++; return 'A'; };
function Box() {
const r = useRef(expensive());
const [, force] = useState(0);
const hit = () => { r.current = 'B'; force((k) => k + 1); };
return <button onClick={hit}>{r.current}</button>;
}
function Quiet() {
const r = useRef(0);
return <button onClick={() => { r.current++; }}>n={r.current}</button>;
}
// 5. Two clicks on the button. What is `renders` — and what is it once the React
// Compiler is enabled for this file, with nothing else changed?
let renders = 0;
const Row = memo(function Row({ style }) { renders++; return <li style={style}>Row</li>; });
function List() {
const [n, setN] = useState(0);
return <ul><button onClick={() => setN(n + 1)}>{n}</button>
<Row style={{ color: 'red' }} /></ul>;
}
// 6. One of these two components is fine and the other hangs the page. Which, and why?
const cached = Promise.resolve('cached');
const Fresh = () => <b>{use(new Promise((r) => setTimeout(() => r('fresh'), 5)))}</b>;
const Cheap = ({ on }) => <i>{on ? use(cached) : 'no promise'}</i>;
root.render(<Suspense fallback={<p>loading</p>}><Cheap on /><Fresh /></Suspense>);// 7. `save` takes 30 ms. During those 30 ms, what does the <p> say, and the button?
function Child() {
const { pending } = useFormStatus();
return <button>{pending ? 'Saving' : 'Save'}</button>;
}
function Outer() {
const s = useFormStatus();
return <form action={save}><p>{String(s.pending)}</p><Child /></form>;
}
// 8. Which element renders at each of /products/new, /products/p-9/reviews and
// /products/a/b? What does useParams() return in the second and third cases?
<Routes>
<Route path="/products/:sku" element={<Detail />} />
<Route path="/products/new" element={<New />} />
<Route path="/products/:sku/reviews/:page?" element={<Reviews />} />
<Route path="/products/*" element={<Browse />} />
</Routes>// 9. `touch` returns the state untouched and `add` pushes into it. What are the four
// values, and why is the last one not 3?
const slice = createSlice({ name: 'cart', initialState: { items: [] },
reducers: { add: (s, a) => { s.items.push(a.payload); }, touch: (s) => s } });
let runs = 0;
const total = createSelector([(s) => s.cart.items], (i) => { runs++; return i.length; });
const a = store.getState().cart;
total(store.getState());
store.dispatch(slice.actions.touch());
const b = store.getState().cart;
total(store.getState());
store.dispatch(slice.actions.add('pen'));
total(store.getState());
console.log(a === b, a.items.length, slice.actions.add('x').type, runs);
// 10. Two of these five queries throw. Which two, what does the third return, and
// which query would reach the input's value?
render(<form><label htmlFor="pw">Password</label>
<input id="pw" type="password" defaultValue="hunter2" />
<button disabled>Sign in</button></form>);
screen.getByRole('textbox', { name: 'Password' });
screen.getByLabelText('Password');
screen.queryByText('hunter2');
screen.getByText('hunter2');
screen.getByRole('button', { name: 'Sign in' });