Test that a memoized component is not rerendered

52 views Asked by At

In my last commit, I degraded the performance of my React app (I introduced Context, and I learned that it interfers with Memoization).

I would like to add tests that checks that a memoized component is not rerenderer after a user action.

App.jsx

import {MemoizedChildComponent} from "./ChildComponent"

export function App() {
 return (
  <div>
   ...
   <MemoizedChildComponent prop1={...} prop2../>
   ...
  </div>
 )
}

ChildComponent.jsx

import {memo} from "react"

function ChildComponent({prop1, prop2,...}) {
 return (
   ...
 )
}

const MemoizedChildComponent = memo(ChildComponent);

export { MemoizedChildComponent};

Using Jest & React Testing Library, I would like to write this test :

import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { App } from "./App"

test("ChildComponent was not rerendered", async () => {
  render(<App>);
  await userEvent.click(...)

  // Assert that ChildComponent was not rerendered
}

I searched in the React Testing Library API and I could not find a way to listen to render events. I thought about Jest mocks, but I don't want to replace ChildComponent, but listen to its render events.

Do you have a proposal to write this test?

1

There are 1 answers

0
H-H On BEST ANSWER

I was finally able to make it work. This SO answer gave the info I needed to access the underlying component of a Memoized component.

So my solution

import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { App } from "./App"
import * as ChildComponentModule from "../../RecordRow/RecordRow";


jest.mock("./ChildComponent", () => {
  const ChildComponentModule = jest.requireActual("./ChildComponent");
  return {
    ...ChildComponentModule, // Unnecessary if there's only 1 exported item
    MemoizedChildComponent : {
      ...ChildComponentModule.MemoizedChildComponent,
      type: jest.spyOn(ChildComponentModule.MemoizedChildComponent, "type"), // Spy on the underlying ChildComponent of MemoizedChildComponent
    },
  };
});

test("ChildComponent was not rerendered", async () => {
  render(<App>);
  jest.clearAllMocks(); // ignore initial renders
  await userEvent.click(...)

  // Assert that ChildComponent was not rerendered
expect(ChildComponentModule.MemoizedChildComponent.type).not.toHaveBeenCalled();

}

In my case, I use named exports. For default exports, you may use this syntax