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

如何测试使用useFormState的Next.js组件?含替代方案

问题

开发Next.js项目时,组件使用了react-dom的useFormState钩子,用Jest编写单元测试时出现错误:

TypeError: (0 , _reactdom.useFormState) is not a function or its return value is not iterable

组件简化代码:

// Inside a Next.js project
import { useState } from 'react'
import { useFormState } from 'react-dom'

export default function MyFormComponent() {
  const [state, formAction] = useFormState(/* ... */);
  // ... rest of the component
}

测试代码:

import '@testing-library/jest-dom'
import { render, screen } from '@testing-library/react'
import MyCustomComponent from '../page'

describe('MyCustomComponent', () => {
    it('renders a heading', () => {
        render(<MyCustomComponent/>);

        screen.debug();
    })
})

解决方案

一、有效测试使用useFormState的Next.js组件

1. 正确模拟useFormState钩子

useFormState是React 18.2+和Next.js 13+的特性,Jest测试时需要手动模拟该钩子,确保返回可迭代的数组(状态+处理函数)。在测试文件顶部添加以下模拟代码:

jest.mock('react-dom', () => ({
  ...jest.requireActual('react-dom'),
  useFormState: jest.fn(() => [/* 自定义初始状态,比如{}或'' */, jest.fn()])
}));

该模拟会保留react-dom的其他原生导出,仅替换useFormState,避免破坏其他功能。

2. 细化模拟适配业务逻辑

如果组件中useFormState绑定了特定的表单处理函数,可以让模拟逻辑更贴近真实行为,比如让返回的formAction返回Promise:

jest.mock('react-dom', () => ({
  ...jest.requireActual('react-dom'),
  useFormState: jest.fn((action, initialState) => [
    initialState,
    jest.fn(() => Promise.resolve(initialState))
  ])
}));

这样可以支持测试异步提交场景,更符合真实业务流程。

3. 确保测试环境配置正确

Next.js默认的Jest配置已适配React/React DOM环境,若自定义了jest.config.js,需注意:

  • 不要手动修改React版本依赖
  • 确保babel-jest或swc-jest正确处理Next.js模块转换

二、易测试的Next.js表单状态管理替代方案与最佳实践

1. React原生useState+手动提交逻辑

对于简单表单,直接用useState管理输入值,手动处理提交,无需额外依赖,测试成本最低:

export default function MyFormComponent() {
  const [email, setEmail] = useState('');
  
  const handleSubmit = async (e) => {
    e.preventDefault();
    // 执行提交逻辑,比如调用Server Action
  };

  return (
    <form onSubmit={handleSubmit}>
      <input 
        type="email" 
        value={email} 
        onChange={(e) => setEmail(e.target.value)} 
      />
      <button type="submit">提交</button>
    </form>
  );
}

测试时可直接模拟输入、触发提交,断言状态变化或提交行为。

2. 使用react-hook-form

社区主流表单库,体积轻量、性能优异,且测试友好。提供专门的测试工具,无需复杂钩子模拟:

import { useForm } from 'react-hook-form';

export default function MyFormComponent() {
  const { register, handleSubmit } = useForm();
  
  const onSubmit = (data) => {
    // 处理表单数据,比如调用接口
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('email', { required: true })} />
      <button type="submit">提交</button>
    </form>
  );
}

测试时用@testing-library/react模拟输入、触发提交,直接断言表单数据是否正确收集。

3. Server Actions结合useState

若需要后端处理逻辑,可分离前端状态与Server Action:用useState管理前端输入,提交时直接调用Server Action,避免依赖useFormState,测试时只需模拟Server Action即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 00:10:34