如何用Jest和React Testing Library测试记忆化组件不重渲染?
测试React记忆化组件是否避免重渲染的方案
背景:提交代码后发现React应用性能下降,排查后确认是引入的Context干扰了组件的Memoization效果。现在需要编写测试,验证用户操作后,被
memo包裹的子组件不会发生重渲染。
现有代码结构
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 };
测试框架代码
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(/* 触发状态变化的元素 */) // 需在此断言ChildComponent未重渲染 })
疑问:查阅React Testing Library API未找到监听渲染事件的方法;考虑过Jest mock,但不想替换原组件,只想监听渲染次数,有没有可行方案?
方案1:通过Jest Mock临时添加渲染计数(不修改生产代码)
该方案通过mock子组件模块,在保留原组件逻辑和memo特性的前提下,添加渲染次数统计,完全不影响生产代码。
测试代码实现
import React from 'react'; import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { App } from "./App"; // Mock ChildComponent模块,注入渲染计数逻辑 jest.mock('./ChildComponent', () => { const originalModule = jest.requireActual('./ChildComponent'); const renderCount = { current: 0 }; // 包装原组件函数,每次调用时递增计数 const SpyWrappedChild = (...args) => { renderCount.current += 1; return originalModule.ChildComponent(...args); }; // 用memo重新包装带计数的组件,保留原记忆化逻辑 const MemoizedChildComponent = React.memo(SpyWrappedChild); // 导出原模块内容,替换目标组件并新增计数变量 return { ...originalModule, MemoizedChildComponent, renderCount, }; }); // 导入mock后的计数变量 import { renderCount } from './ChildComponent'; test("ChildComponent was not rerendered", async () => { render(<App />); // 初始渲染后,子组件应渲染1次 expect(renderCount.current).toBe(1); // 触发用户操作(比如点击会改变父组件/Context状态的元素) await userEvent.click(screen.getByText('触发操作的按钮')); // 断言渲染次数未增加,说明memo生效,未重渲染 expect(renderCount.current).toBe(1); });
方案2:给组件添加测试专用渲染计数(需修改少量代码)
如果不想使用mock,可以在子组件中添加仅在测试环境生效的计数逻辑,通过ref传递统计次数。
1. 修改ChildComponent.jsx
import { memo, useEffect } from "react" function ChildComponent({ prop1, prop2, testRenderCountRef }) { // 仅在测试环境统计渲染次数 useEffect(() => { if (testRenderCountRef) { testRenderCountRef.current += 1; } }); return ( {/* 原组件内容 */} ) } const MemoizedChildComponent = memo(ChildComponent); export { MemoizedChildComponent };
2. 修改App.jsx
import { MemoizedChildComponent } from "./ChildComponent" export function App({ testRenderCountRef }) { return ( <div> {/* 其他业务代码 */} <MemoizedChildComponent prop1={...} prop2={...} testRenderCountRef={testRenderCountRef} /> {/* 其他业务代码 */} </div> ) }
3. 编写测试
import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { App } from "./App" test("ChildComponent was not rerendered", async () => { const renderCountRef = { current: 0 }; render(<App testRenderCountRef={renderCountRef} />); expect(renderCountRef.current).toBe(1); await userEvent.click(screen.getByText('触发操作的按钮')); expect(renderCountRef.current).toBe(1); });
内容的提问来源于stack exchange,提问作者H-H
相关产品推荐
相关产品推荐

