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

Jest测试中React组件onBlur配合useState Hook不生效如何解决?

问题根因
  • Enzyme的shallow渲染对React Hook的支持存在兼容性缺陷,无法正确处理连续状态更新下的闭包值捕获。React的状态更新是批量异步执行的,你在同一个执行栈内先调用onChange触发setCardNumber,此时组件还没有重渲染,handleOnBlur函数闭包绑定的还是初始渲染时的cardNumber值(空字符串),所以调用setCardType的时候传入的是空值,本质上setCardType已经执行,只是传入的值不符合预期。
  • act的使用方式错误:你这里的状态更新都是同步逻辑,不需要用async/await包裹act,多余的异步逻辑反而会影响Enzyme的状态同步。
  • 连续事件调用没有拆分渲染周期:onChange触发后必须等组件完成重渲染,再获取最新的input实例调用onBlur,才能拿到更新后的cardNumber值。
修复方案

方案1:更换为React官方推荐的@testing-library/react(更推荐)

该测试库天然支持Hook的状态更新,更贴近用户实际使用场景,修正后的测试代码如下:

import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import { Test } from '.';

describe('Sandbox Test', () => {
  it('should set cardNumber when doing onChange', () => {
    render(<Test />);
    const input = screen.getByRole('textbox');
    fireEvent.change(input, { target: { value: '123456' } });
    expect(screen.getByText('CardNumber: 123456')).toBeInTheDocument();
    expect(screen.getByText('CardType: ')).toBeInTheDocument();
  });

  it('should set cardType when doing onBlur', () => {
    render(<Test />);
    const input = screen.getByRole('textbox');
    fireEvent.change(input, { target: { value: '123456' } });
    fireEvent.blur(input);
    expect(screen.getByText('CardNumber: 123456')).toBeInTheDocument();
    expect(screen.getByText('CardType: 123456')).toBeInTheDocument();
  });
});

方案2:保留Enzyme写法,修改为mount渲染并拆分事件调用

如果必须使用Enzyme,将shallow改为mount(完整渲染),并且拆分两次事件的执行周期,每次更新后重新获取元素实例:

import React from 'react';
import { mount } from 'enzyme';
import { Test } from '.';
import { act } from 'react-dom/test-utils';

describe('Sandbox Test', () => {
  it('should set cardNumber when doing onChange', () => {
    const component = mount(<Test />);
    act(() => {
      component.find('input').props().onChange({ target: { value: '123456' } });
    });
    component.update();
    expect(component.find('#cn').text()).toEqual(`CardNumber: 123456`);
    expect(component.find('#ct').text()).toEqual(`CardType: `);
  });

  it('should set cardType when doing onBlur', () => {
    const component = mount(<Test />);
    // 先执行change,更新状态重渲染
    act(() => {
      component.find('input').props().onChange({ target: { value: '123456' } });
    });
    component.update();
    // 重渲染后再获取最新的input实例调用blur,此时闭包绑定的是最新的cardNumber
    act(() => {
      component.find('input').props().onBlur();
    });
    component.update();
    expect(component.find('#cn').text()).toEqual(`CardNumber: 123456`);
    expect(component.find('#ct').text()).toEqual(`CardType: 123456`);
  });
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 04:15:05