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

React组件State Hook测试异常:setCheck未被调用排查

React Hook useState 测试不生效问题排查

组件代码

import React, { SyntheticEvent, useEffect, useState } from 'react';
import { Checkbox, Grid, Header } from 'semantic-ui-react';

interface IIdentityItem {
  name: string,
  comment: string,
  checked: boolean,
  handleSetCheckState: any,
  index: number
};


export default ({ name, comment, checked, handleSetCheckState, index }: IIdentityItem) => {

  const [check, setCheck] = useState(true);

  useEffect(() => {
    setCheck(checked);
  }, []);

  const onChange = (e: SyntheticEvent, data: object) => {
    setCheck(!check);
    handleSetCheckState(index, !check);
  };

  return (
    <Grid className='p-16 py-9 bg-white'>
      <Grid.Column width='eleven' textAlign='left'>
        <Header as='p' className='description'>{name}</Header>
        <Header as='p' className='comment'>{comment}</Header>
      </Grid.Column>
      <Grid.Column width='five' verticalAlign='middle'>
        <Checkbox toggle checked={check} onChange={onChange} />
      </Grid.Column>
    </Grid>
  )
}

原测试代码

import ChainItem from './ChainItem';
import React from 'react';
import { create, act } from 'react-test-renderer';
import { BrowserRouter } from 'react-router-dom';
import { Checkbox } from 'semantic-ui-react';

const useStateSpy = jest.spyOn(React, "useState");

describe('ChainItem', () => {
  let handleSetCheckState, index;
  beforeEach(() => {
    handleSetCheckState = jest.fn();
    index = 0;
    //useStateSpy.mockReturnValueOnce([true, setCheck]);
  });
  it('should work', () => {
    let tree;
    act(() => {
      tree = create(
        <ChainItem
          handleSetCheckState={handleSetCheckState}
          index={index} />
      );
    });
    expect(tree).toMatchSnapshot();
  });
  it('functions ', () => {
    let setCheck = jest.fn();
    useStateSpy.mockImplementationOnce(function() { return [true, setCheck] });
    let tree;
    act(() => {
      tree = create(
        <ChainItem
          handleSetCheckState={handleSetCheckState}
          checked={true}
          index={index} />
      );
    });
    const items = tree.root.findAllByType(Checkbox);
    act(() => items[0].props.onChange({
      target: {
        value: false
      }
    }));
    expect(setCheck).toHaveBeenCalled();
    expect(handleSetCheckState).toHaveBeenCalledWith(
      index,
      false
    );
  });
  afterAll(() => jest.resetModules());
});

问题描述

触发Checkbox的onChange事件后,mock的setCheck并未被调用,React Hook测试不生效,求排查操作错误。


问题原因

  1. useState Spy 未正确重置:多个测试用例共用同一个全局useStateSpy,第一个测试用例调用了真实的useState,导致第二个测试用例的mockImplementationOnce无法正确覆盖Hook行为。
  2. 事件参数传递不匹配:组件的onChange方法定义了两个参数(SyntheticEvent和data对象),但测试中仅传递了一个参数,可能导致事件处理逻辑异常。
  3. Spy 生命周期管理不当:未在每个测试用例后恢复useState的原始实现,导致Spy状态残留影响后续测试。

修复方案

修复后的测试代码

import ChainItem from './ChainItem';
import React from 'react';
import { create, act } from 'react-test-renderer';
import { Checkbox } from 'semantic-ui-react';

describe('ChainItem', () => {
  let handleSetCheckState, index;
  let useStateSpy: jest.SpyInstance;

  beforeEach(() => {
    handleSetCheckState = jest.fn();
    index = 0;
    // 每个测试用例前重新创建Spy
    useStateSpy = jest.spyOn(React, "useState");
  });

  afterEach(() => {
    // 每个测试用例后恢复useState的原始实现,避免状态残留
    useStateSpy.mockRestore();
  });

  it('should render correctly', () => {
    let tree;
    act(() => {
      tree = create(
        <ChainItem
          name="测试名称"
          comment="测试备注"
          checked={true}
          handleSetCheckState={handleSetCheckState}
          index={index} />
      );
    });
    expect(tree).toMatchSnapshot();
  });

  it('should trigger setCheck and callback when checkbox toggles', () => {
    const mockSetCheck = jest.fn();
    // 固定mock useState的返回值,确保组件使用我们的mock函数
    useStateSpy.mockReturnValue([true, mockSetCheck]);

    let tree;
    act(() => {
      tree = create(
        <ChainItem
          name="测试名称"
          comment="测试备注"
          checked={true}
          handleSetCheckState={handleSetCheckState}
          index={index} />
      );
    });

    // 获取Checkbox组件,传递符合定义的两个参数触发onChange
    const checkbox = tree.root.findByType(Checkbox);
    act(() => {
      checkbox.props.onChange({} as React.SyntheticEvent, {});
    });

    // 断言setCheck被调用,参数为false(!初始true)
    expect(mockSetCheck).toHaveBeenCalledWith(false);
    // 断言父组件回调被正确调用
    expect(handleSetCheckState).toHaveBeenCalledWith(index, false);
  });
});

组件优化建议

  1. 完善useEffect依赖:当前useEffect依赖为空数组,当props.checked更新时不会同步check状态,建议补充依赖:
useEffect(() => {
  setCheck(checked);
}, [checked]); // 添加checked作为依赖,实现props更新时同步状态
  1. 类型安全优化:将handleSetCheckState的any类型替换为具体类型:
interface IIdentityItem {
  name: string;
  comment: string;
  checked: boolean;
  handleSetCheckState: (index: number, checked: boolean) => void; // 明确类型
  index: number;
};

内容的提问来源于stack exchange,提问作者Ivo Sram

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 05:10:21