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

测试React Router 6.4 Action时触发TypeError: request.formData不是函数

问题

在react-router-dom(React Router 6.4)中编写了绑定到路由的action函数,用于根据指定ID获取数据。测试该action时出现错误:TypeError: request.formData is not a function,Node.js 16和18环境下均会出现。

测试代码

it("should return a success response", async () => {
  const mockResponse = {
    response: "success",
    hasError: false,
    status: 200,
    contextType: "GET_DATA_FROM_ID"
  };

  const formData = new FormData();
  formData.append("contextType", "GET_DATA_FROM_ID");

  const myOptions = {
      method: "POST",
      headers: {
        "Content-Type":"application/json",
      },
      body: formData
    };

  let request = new Request(GET_DATA_FROM_ID, myOptions);

  const response = await myAction({ request, params: {} });

  expect(response).toEqual(mockResponse);
});

Action代码

export const myAction = async ({ request }: Request) => {
  try {
    const response = await axios.get(`${URL}${id}`, {
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${accessToken}`,
      },
      withCredentials: false,
    });

    if (response.status === 200) {
      return json({
        response: response.data,
        isError: false,
        status: 200,
        contextType,
      });
    }
  } catch (error: any) {
    return json({
      error,
      isError: true,
      status: error.status,
      contextType,
    });
  }
}

问题原因与解决方法

1. 核心错误原因

你测试中创建的Request是Node.js内置的Request对象(来自node:http模块),而React Router的action期望的是Web标准的Request对象(包含formData()等方法)。Node.js 18之前的内置Request没有实现这些Web标准方法,18版本虽引入相关API,但默认配置下仍可能无法直接使用。

2. 修复测试中的Request问题

有两种可选方案:

  • 使用Web标准Request polyfill:安装@whatwg-node/fetch,它提供完全符合Web标准的Request实现,替换Node.js内置版本。在测试文件顶部引入:
    import { Request } from '@whatwg-node/fetch';
    
    此后创建的Request对象会自带formData()方法。
  • 修正请求配置矛盾:测试中同时设置了Content-Type: application/json和body: FormData,这本身不符合HTTP规范。如果action需要解析表单数据,应去掉手动设置的Content-Type(浏览器/标准Request会自动添加multipart/form-data头);如果action解析JSON,就将body改为JSON字符串:
    const myOptions = {
      method: "POST",
      headers: { "Content-Type":"application/json" },
      body: JSON.stringify({ contextType: "GET_DATA_FROM_ID" })
    };
    

3. 修正Action的类型定义

你的Action参数类型错误,React Router的action接收的是包含request和params的对象,而非直接的Request类型。应使用React Router提供的ActionFunctionArgs类型:

import type { ActionFunctionArgs } from 'react-router-dom';

export const myAction = async ({ request, params }: ActionFunctionArgs) => {
  // 现有逻辑保持不变
}

测试React Router 6.4+ Action的其他方法

方法1:使用React Router测试组件渲染

通过MemoryRouter渲染路由,模拟表单提交或请求触发action:

import { render } from '@testing-library/react';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import { myAction } from './path-to-action';

it('triggers action correctly', async () => {
  render(
    <MemoryRouter initialEntries={['/your-route']}>
      <Routes>
        <Route path="/your-route" action={myAction} element={<div>Test</div>} />
      </Routes>
    </MemoryRouter>
  );

  // 模拟请求触发action
  const response = await fetch('/your-route', {
    method: 'POST',
    body: JSON.stringify({ contextType: "GET_DATA_FROM_ID" }),
    headers: { 'Content-Type': 'application/json' }
  });

  const data = await response.json();
  expect(data).toEqual({
    response: "success",
    isError: false,
    status: 200,
    contextType: "GET_DATA_FROM_ID"
  });
});

方法2:Mock依赖直接测试逻辑

如果Action中调用了axios等外部依赖,直接mock这些依赖,避免处理Request对象的复杂问题:

import axios from 'axios';
import { myAction } from './path-to-action';
import { Request } from '@whatwg-node/fetch';

jest.mock('axios');

it('returns success response', async () => {
  // Mock axios响应
  (axios.get as jest.Mock).mockResolvedValue({
    status: 200,
    data: 'success'
  });

  // 创建符合标准的Request对象
  const request = new Request('/test', {
    method: 'POST',
    body: JSON.stringify({ contextType: "GET_DATA_FROM_ID" })
  });

  const response = await myAction({ request, params: {} });
  const responseData = await response.json();

  expect(responseData).toEqual({
    response: 'success',
    isError: false,
    status: 200,
    contextType: "GET_DATA_FROM_ID"
  });
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 22:15:59