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

React Hook测试异常:状态未始终更新,测试执行顺序影响结果

问题分析与解决方案

核心问题根源

  1. 测试用例共享Hook实例导致状态污染:你在describe外部调用renderHook,所有测试复用同一个Hook实例,前一个测试的状态会残留并影响后续测试,这就是为什么调整测试顺序结果会变化的原因。
  2. 直接修改状态对象未触发React更新:toggleSelect里直接修改event.selected属于可变操作,没有通过setEventList更新useState的状态,导致React无法感知到变化,Hook的状态也没有真正更新。

第一步:修复Hook的状态更新逻辑

修正toggleSelect函数

必须通过setEventList生成新的数组和对象,触发React状态更新:

const toggleSelect = (event: CalendarEventType): void => {
  setEventList(prevState => 
    prevState.map(item => 
      // 找到目标事件,创建新对象修改selected属性,其余事件保持不变
      item.id === event.id ? {...item, selected: !item.selected} : item
    )
  );
};

优化addEvent函数

避免修改传入的原始对象,创建新对象后再添加:

const addEvent = (event: CalendarEventType): void => {
  // 复制传入的event并添加id,防止污染原始对象
  const newEvent = {...event, id: uuid()};
  setEventList(prevState => [...prevState, newEvent]);
};

第二步:修改测试代码,保证用例独立性

每个测试用例都重新渲染Hook,确保初始状态干净,不受其他测试影响:

describe('Event management', () => {
  // 每个测试单独初始化Hook实例
  test('Select and unselect event', async () => {
    const { result } = renderHook((initialProps) => useCalendarEvent(initialProps), {
      initialProps: exampleEvent
    });
    
    result.current.toggleSelect(result.current.eventList[0]);
    // 用waitFor等待异步状态更新完成
    await waitFor(() => {
      expect(result.current.eventList[0].selected).toBeTruthy();
    });
  });

  test('Add event', async () => {
    const { result } = renderHook((initialProps) => useCalendarEvent(initialProps), {
      initialProps: exampleEvent
    });
    
    await act(async () => {
      result.current.addEvent({title: 'TestAdd', start: new Date()});
    });
    
    expect(result.current.eventList).toHaveLength(2);
  });

  test('log current event list', () => {
    const { result } = renderHook((initialProps) => useCalendarEvent(initialProps), {
      initialProps: exampleEvent
    });
    console.log(result.current.eventList);
  });
});

可选:用beforeEach统一初始化

如果想让代码更简洁,可以用beforeEach在每个测试前重置Hook实例:

describe('Event management', () => {
  let hookResult;

  beforeEach(() => {
    hookResult = renderHook((initialProps) => useCalendarEvent(initialProps), {
      initialProps: exampleEvent
    });
  });

  test('Select and unselect event', async () => {
    const { result } = hookResult;
    result.current.toggleSelect(result.current.eventList[0]);
    
    await waitFor(() => {
      expect(result.current.eventList[0].selected).toBeTruthy();
    });
  });

  test('Add event', async () => {
    const { result } = hookResult;
    await act(async () => {
      result.current.addEvent({title: 'TestAdd', start: new Date()});
    });
    
    expect(result.current.eventList).toHaveLength(2);
  });

  test('log current event list', () => {
    const { result } = hookResult;
    console.log(result.current.eventList);
  });
});

额外说明

  • 使用waitFor:React状态更新是异步的,测试中需要等待状态更新完成后再做断言,否则可能拿到更新前的旧状态。
  • 测试独立性:所有测试用例都应该从干净的初始状态开始,避免用例之间的状态污染,这是保证测试结果可靠的基础。

内容的提问来源于stack exchange,提问作者Alex Perrot

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 17:56:28