测试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
相关产品推荐
相关产品推荐

