React Router 6.4如何在action函数内使用useDispatch?
问题场景
我在React Router 6.4中配置了如下路由:
{ path: "/user/login", element: <Login />, action: loginAction, }
提交登录表单能正常触发loginAction,但在这个异步action函数里调用useDispatch时触发报错:
async function action({ request }) { const formData = await request.formData(); const dispatch = useDispatch(); // 此处报错 }
报错信息:
Line 38:20: React Hook "useDispatch" is called in function "action" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use" react-hooks/rules-of-hooks
把函数名改成Action后不再报错,但功能还是无法正常运行,请问该如何在React Router 6.4的action函数中正确使用useDispatch?
解决方案
React Router的action函数不属于React组件或自定义Hook范畴,绝对不能直接在里面使用React Hooks(包括useDispatch)。正确的做法是直接引入你的Redux store实例,调用store.dispatch()来触发状态更新。
具体步骤
- 确保你的Redux store是单独导出的,比如在
store.js中:
import { configureStore } from '@reduxjs/toolkit'; import authReducer from './slices/authSlice'; // 导出store实例 export const store = configureStore({ reducer: { auth: authReducer, }, });
- 在你的登录action函数文件中导入store,直接调用dispatch:
import { store } from '../store'; // 根据你的实际文件路径调整 import { loginSuccess } from '../slices/authSlice'; // 替换成你的登录状态更新action async function loginAction({ request }) { const formData = await request.formData(); const username = formData.get('username'); const password = formData.get('password'); // 执行登录请求逻辑 try { const response = await fetch('/api/login', { method: 'POST', body: JSON.stringify({ username, password }), headers: { 'Content-Type': 'application/json' }, }); const data = await response.json(); // 直接用store.dispatch触发Redux状态更新 store.dispatch(loginSuccess(data.user)); // 登录成功后重定向到首页或仪表盘 return redirect('/dashboard'); } catch (err) { return { error: '登录失败,请检查账号密码' }; } }
为什么改函数名没用?
把函数名改成Action只是绕过了ESLint的Hook规则校验,但本质上这个函数依然不满足Hook的使用条件——Hook只能在React组件的顶层代码或自定义Hook中调用。所以即使不报错,useDispatch也无法获取到Redux的上下文,自然没法正常触发reducer更新状态。
内容的提问来源于stack exchange,提问作者leonSantoz

