You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 08:03:15