Skip to the code
cf.

Lazy-loaded sales chart

The tea shop's sales page has a chart whose code is large, and most visitors never open it. Build SalesPage, the default export, so the chart's code is only fetched when someone asks for it.

It takes loadChart, a function that returns a promise of a module like { default: SalesChart } (in a real app, () => import("./SalesChart.jsx")), and sales, an array of numbers to pass to the chart. Both have defaults in the starter, so the Preview works.

Show a heading Weekly sales and a Show chart button. Don't call loadChart before the button is pressed. When it's pressed, the button says Hide chart and the chart appears below it as <Chart sales={sales} />, where Chart is made with lazy(loadChart). While its code loads, show Loading chart… (with the single … character) in its place; the heading and the button stay.

Pressing Hide chart removes the chart and the button says Show chart again. Showing it again must not call loadChart again, and shows the chart at once, with no fallback. Call lazy once per SalesPage, not on every render.

<SalesPage loadChart={() => import("./SalesChart.jsx")} sales={[12, 18, 9]} />
 
// Weekly sales
// [Show chart]    → pressed →   [Hide chart]
//                               Loading chart…   → then the chart

What the tests check

  1. doesn't load the chart before Show chart is pressed
    const loadChart = spy(() => new Promise(() => {}));
    render(<App loadChart={loadChart} sales={[1, 2]} />);
    await flush();
    expect(screen.getByRole("heading", { name: "Weekly sales" })).toBeInTheDocument();
    expect(screen.getByRole("button", { name: "Show chart" })).toBeInTheDocument();
    expect(loadChart).toHaveBeenCalledTimes(0);
    expect(screen.queryByText("Loading chart…")).not.toBeInTheDocument();
    
  2. shows Loading chart… while the code loads, and keeps the page
    const loadChart = spy(() => new Promise(() => {}));
    render(<App loadChart={loadChart} sales={[1, 2]} />);
    await user.click(screen.getByRole("button", { name: "Show chart" }));
    expect(loadChart).toHaveBeenCalledTimes(1);
    expect(screen.getByText("Loading chart…")).toBeInTheDocument();
    expect(screen.getByRole("heading", { name: "Weekly sales" })).toBeInTheDocument();
    expect(screen.getByRole("button", { name: "Hide chart" })).toBeInTheDocument();
    
  3. shows the chart with the sales when its code arrives
    const chartModule = deferred();
    render(<App loadChart={() => chartModule.promise} sales={[12, 18, 9]} />);
    await user.click(screen.getByRole("button", { name: "Show chart" }));
    chartModule.resolve({ default: ({ sales }) => <p>Chart of {sales.join(", ")}</p> });
    expect(await screen.findByText("Chart of 12, 18, 9")).toBeInTheDocument();
    expect(screen.queryByText("Loading chart…")).not.toBeInTheDocument();
    
  4. Hide chart removes the chart
    render(<App loadChart={async () => ({ default: () => <p>The chart</p> })} sales={[1]} />);
    await user.click(screen.getByRole("button", { name: "Show chart" }));
    expect(await screen.findByText("The chart")).toBeInTheDocument();
    await user.click(screen.getByRole("button", { name: "Hide chart" }));
    expect(screen.queryByText("The chart")).not.toBeInTheDocument();
    expect(screen.getByRole("button", { name: "Show chart" })).toBeInTheDocument();
    
  5. showing it again doesn't load it again
    const loadChart = spy(async () => ({ default: () => <p>The chart</p> }));
    render(<App loadChart={loadChart} sales={[1]} />);
    await user.click(screen.getByRole("button", { name: "Show chart" }));
    expect(await screen.findByText("The chart")).toBeInTheDocument();
    await user.click(screen.getByRole("button", { name: "Hide chart" }));
    await user.click(screen.getByRole("button", { name: "Show chart" }));
    expect(screen.getByText("The chart")).toBeInTheDocument();
    expect(screen.queryByText("Loading chart…")).not.toBeInTheDocument();
    expect(loadChart).toHaveBeenCalledTimes(1);
    
  6. new sales don't load the code again
    const loadChart = spy(async () => ({ default: ({ sales }) => <p>Chart of {sales.join(", ")}</p> }));
    const { rerender } = render(<App loadChart={loadChart} sales={[1, 2]} />);
    await user.click(screen.getByRole("button", { name: "Show chart" }));
    expect(await screen.findByText("Chart of 1, 2")).toBeInTheDocument();
    rerender(<App loadChart={loadChart} sales={[3, 4]} />);
    expect(screen.getByText("Chart of 3, 4")).toBeInTheDocument();
    expect(loadChart).toHaveBeenCalledTimes(1);
    

Learn more

App.jsxSaved in this browser

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).