The tea shop's order page has cup buttons and a note box, and both child components are already wrapped in memo. Yet typing a note renders the cup buttons again on every keystroke, and adding a cup renders the note box again. The cause is in the parent: it hands its children a new function on every render, so memo always sees a changed prop. The starter is that code. Fix TeaOrder so each child renders only when something it shows has changed.
Keep what it does. TeaOrder takes price, the price of one cup (4 by default), and an optional onRender. It shows Cups: 0 and Total: $0.00 at first. CupControls has two buttons, Add a cup and Remove a cup, which change the count by one; the count never goes below 0. NoteField is a text box labelled Note for the barista.
CupControls and NoteField stay as they are. Each calls onRender with its own name ("CupControls" or "NoteField") every time it renders, which is how the tests count renders. Don't remove their memo and don't change them: the fix belongs in TeaOrder.
What to fix: typing a note never renders CupControls again, and renders NoteField once per keystroke (it shows the note). Adding or removing cups renders neither child again: the count and total are shown by TeaOrder itself. Rendering TeaOrder again with the same props renders neither child.
The tests render without Strict Mode, so each update means exactly one render. Export TeaOrder by name. The default export, App, renders it and logs each child render for the Preview.
<TeaOrder price={4} onRender={(name) => console.log(name)} />
// after adding two cups:
<p>Cups: 2</p>
<p>Total: $8.00</p>
<button>Add a cup</button> <button>Remove a cup</button>
<label>Note for the barista <input value="" /></label>
render(<TeaOrder />);
expect(screen.getByText("Cups: 0")).toBeInTheDocument();
expect(screen.getByText("Total: $0.00")).toBeInTheDocument();
await user.click(screen.getByRole("button", { name: "Add a cup" }));
await user.click(screen.getByRole("button", { name: "Add a cup" }));
expect(screen.getByText("Cups: 2")).toBeInTheDocument();
expect(screen.getByText("Total: $8.00")).toBeInTheDocument();
await user.click(screen.getByRole("button", { name: "Remove a cup" }));
expect(screen.getByText("Cups: 1")).toBeInTheDocument();
expect(screen.getByText("Total: $4.00")).toBeInTheDocument();
render(<TeaOrder />);
await user.click(screen.getByRole("button", { name: "Add a cup" }));
await user.click(screen.getByRole("button", { name: "Remove a cup" }));
await user.click(screen.getByRole("button", { name: "Remove a cup" }));
expect(screen.getByText("Cups: 0")).toBeInTheDocument();
await user.click(screen.getByRole("button", { name: "Add a cup" }));
expect(screen.getByText("Cups: 1")).toBeInTheDocument();
render(<TeaOrder price={3.5} />);
for (let i = 0; i < 3; i++) await user.click(screen.getByRole("button", { name: "Add a cup" }));
expect(screen.getByText("Total: $10.50")).toBeInTheDocument();
const renders = { CupControls: 0, NoteField: 0 };
render(<TeaOrder onRender={(name) => renders[name]++} />);
expect(renders).toEqual({ CupControls: 1, NoteField: 1 });
await user.type(screen.getByLabelText("Note for the barista"), "oat");
expect(screen.getByLabelText("Note for the barista")).toHaveValue("oat");
expect(renders.NoteField).toBe(4);
const renders = { CupControls: 0, NoteField: 0 };
render(<TeaOrder onRender={(name) => renders[name]++} />);
await user.type(screen.getByLabelText("Note for the barista"), "no sugar");
expect(renders.CupControls).toBe(1);
const renders = { CupControls: 0, NoteField: 0 };
render(<TeaOrder onRender={(name) => renders[name]++} />);
for (let i = 0; i < 3; i++) await user.click(screen.getByRole("button", { name: "Add a cup" }));
expect(screen.getByText("Cups: 3")).toBeInTheDocument();
expect(renders).toEqual({ CupControls: 1, NoteField: 1 });
const renders = { CupControls: 0, NoteField: 0 };
render(<TeaOrder onRender={(name) => renders[name]++} />);
await user.click(screen.getByRole("button", { name: "Add a cup" }));
await user.click(screen.getByRole("button", { name: "Add a cup" }));
await user.click(screen.getByRole("button", { name: "Remove a cup" }));
expect(screen.getByText("Cups: 1")).toBeInTheDocument();
expect(renders).toEqual({ CupControls: 1, NoteField: 1 });
const renders = { CupControls: 0, NoteField: 0 };
const onRender = (name) => renders[name]++;
const { rerender } = render(<TeaOrder price={4} onRender={onRender} />);
await user.click(screen.getByRole("button", { name: "Add a cup" }));
rerender(<TeaOrder price={4} onRender={onRender} />);
expect(screen.getByText("Cups: 1")).toBeInTheDocument();
expect(renders).toEqual({ CupControls: 1, NoteField: 1 });
Run tests runs every test on your code, here in your browser. The problem is solved when they all pass.
Ctrl + Enter runs the tests (⌘ + Enter on a Mac).
I'm here to help with Stable callbacks for memo children. I give hints, never the solution, so you get to solve it yourself. Ask for a hint, select code to ask about it, or press Ask Tuto why on a failing test.
Your first 3 answers need no account. for free to keep asking.