Blog, COLDSURF

Things to Watch Out for When Using React.memo (feat. Accessing Array Items by Index)

#react

This post reflects on an issue encountered while using React.memo in a project.
The initial code was as follows:
export default memo(ProjectItemList, (prev, next) => { return prev.data.length === next.data.length && prev.data[0].id === next.data[0].id; });
The issue stemmed from not handling the case when the array has zero items. If the number of user-created projects was zero, and the component re-rendered, an error would occur because of the missing data.
Thus, I modified the code to handle this scenario:
export default memo(ProjectItemList, (prev, next) => { const prevFirstId = prev.data?.[0]?.id; const nextFirstId = next.data?.[0]?.id; return prev.data.length === next.data.length && prevFirstId === nextFirstId; });

Testing Issue

The error wasn't caught during testing because the tests only used render from the @testing-library/react library, which doesn't trigger React.memo’s areEqual function. Additionally, there was no test case for an empty array (i.e., array length of 0). The tests only assumed the array length would be greater than 1, so the issue with empty arrays wasn't detected.
To address this, I updated the tests to use rerender, which forces React.memo to compare props again and run the areEqual callback.
Here’s how the test was modified:
import { screen, render } from "@testing-library/react"; ... ... describe("ProjectItemList", () => { it("renders with an empty data array", () => { const { rerender } = render( <ProjectItemList data={fakeProjectList} // Initially, the array has length 0 renderItem={(item) => <ProjectItem key={item.id} data={item} />} />, { wrapper: AllTheProviders, } ); expect(screen.getByTestId("project-item-list").children.length).toBe(0); // Use rerender to trigger React.memo and execute the areEqual callback inside memo rerender( <ProjectItemList data={fakeProjectList} renderItem={(item) => <ProjectItem key={item.id} data={item} />} /> ); expect(screen.getByTestId("project-item-list").children.length).toBe(0); }); });

Conclusion

  • When accessing items in an array by index, always ensure proper validation to handle edge cases (like empty arrays).
  • When testing components using React.memo, use the rerender function from @testing-library/react to trigger the areEqual callback and effectively test the memoization logic.
← Go home