React Testing Library rerender时useRef值被重置问题求助
问题:RTL rerender时useRef值被重置导致断言失败
我需要在组件重新渲染时基于ref值断言函数执行情况,但调用React Testing Library(RTL)的rerender方法时,resetPagination的useRef值被重置为false。在useEffect中添加日志后发现,rerender时组件会触发卸载操作。
组件代码
const resetPagination = useRef(false); useEffect(() => { if (resetPagination.current) { resetList(); resetScroll(); resetPagination.current = false; } else { updateList(); } }, [filteredList]);// filteredList是组件props,updateFilter会根据更新的过滤器修改filteredList const onFilterChange = (index, value, isSearch) => { updateFilter(index, value, isSearch); resetPagination.current = true; };
测试用例代码
it('reset list on resetPagination(filter change)', async () => { const { container, rerender } = renderComponent(pipelines); const filtersBtn = queryByDataId( container, `${migratePipelineFiltersDataIds.applyFilters}-button` ); await act(async () => { await user.click(filtersBtn); }); expect(updateFilterMock).toHaveBeenCalled(); rerender( <PipelinesList pipelinesCount={pipelines.length} filteredPipelines={pipelines} updateFilter={updateFilterMock} updateSyncFrequency={updateSyncFrequencyMock} listFilters={[]} /> ); expect(MOCKED_LIST_ITEMS.resetList).toBeCalled(); });
问题原因
RTL的rerender方法如果传入的组件实例与初始渲染不一致,或者props变化触发组件卸载重建,会重置组件内部所有状态(包括useRef)。当前测试中,rerender时直接创建了新的<PipelinesList>实例,导致原组件被卸载,新组件初始化后resetPagination.current回到初始值false。
解决方法
1. 复用初始组件props
不要在rerender时重新构建组件,复用初始props并修改需要更新的字段,保证组件实例一致:
const initialProps = { pipelinesCount: pipelines.length, filteredPipelines: pipelines, updateFilter: updateFilterMock, updateSyncFrequency: updateSyncFrequencyMock, listFilters: [] }; it('reset list on resetPagination(filter change)', async () => { const { container, rerender } = renderComponent(pipelines); const filtersBtn = queryByDataId( container, `${migratePipelineFiltersDataIds.applyFilters}-button` ); await user.click(filtersBtn); expect(updateFilterMock).toHaveBeenCalled(); // 复用初始props,仅修改需要更新的字段 rerender(<PipelinesList {...initialProps} />); expect(MOCKED_LIST_ITEMS.resetList).toBeCalled(); });
2. 调整组件逻辑,将状态提升到props
把resetPagination的状态从useRef改为父组件管理的state,通过props传递给子组件,避免组件卸载导致状态丢失:
// 父组件 const [shouldResetPagination, setShouldResetPagination] = useState(false); <PipelinesList shouldResetPagination={shouldResetPagination} onResetComplete={() => setShouldResetPagination(false)} // 其他props... /> // 子组件 useEffect(() => { if (shouldResetPagination) { resetList(); resetScroll(); onResetComplete(); } else { updateList(); } }, [filteredList, shouldResetPagination]); const onFilterChange = (index, value, isSearch) => { updateFilter(index, value, isSearch); setShouldResetPagination(true); };
3. 检查renderComponent实现
确保renderComponent返回的是RTL原生的rerender方法,避免自定义逻辑导致组件卸载重建。如果是自定义渲染函数,需调整内部实现以保证rerender时复用组件实例。
内容的提问来源于stack exchange,提问作者psycho_codes
相关产品推荐
相关产品推荐

