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 thererender
function from@testing-library/react
to trigger theareEqual
callback and effectively test the memoization logic.