在createAsyncThunk的async函数中调用useNavigate无效且无报错,寻求帮助
问题原因
你直接在createAsyncThunk的回调函数里调用useNavigate是违反React Hook规则的——useNavigate是React Router提供的Hook,只能在函数组件的顶层代码或者自定义Hook中调用,不能在普通异步函数(比如thunk的payloadCreator)里使用。这就是代码没报错但导航完全无效的核心原因。
解决方案
下面提供三种可行的解决方式,你可以根据项目架构选择:
方式一:调用Thunk时传入navigate函数
修改Thunk,让它接收包含用户信息和navigate函数的参数对象:
import { createAsyncThunk } from '@reduxjs/toolkit'; import axios, { AxiosError } from 'axios'; import { toast } from 'react-toastify'; import { useNavigate } from 'react-router-dom'; import environment from '../path/to/environment'; import type { UserLoginProps } from '../types'; export const loginAdminUser = createAsyncThunk( 'post/login', async ({ user, navigate }: { user: UserLoginProps; navigate: ReturnType<typeof useNavigate> }) => { try { const response = await axios.post(`${environment.BASE_URL}/user/admin-login`, user); console.log('response', response); if (response.data.success) { toast.success(response.data.message); toast('Redirecting to Home page'); localStorage.setItem('token', response.data.data); // 调用传入的navigate函数 navigate('/admin/home'); } else { toast.error(response.data.message); } return response.data; } catch (error: unknown) { const err = error as AxiosError; throw new Error(err.message); } } );
然后在组件中调用Thunk时,把useNavigate返回的函数传进去:
import { useDispatch } from 'react-redux'; import { useNavigate } from 'react-router-dom'; import { loginAdminUser } from '../path/to/thunk'; import type { UserLoginProps } from '../types'; function LoginComponent() { const dispatch = useDispatch(); const navigate = useNavigate(); const handleLogin = (userData: UserLoginProps) => { dispatch(loginAdminUser({ user: userData, navigate })); }; // 组件渲染逻辑... }
方式二:在组件中监听Thunk执行结果后导航
把导航逻辑从Thunk中剥离,放到组件里,通过监听Redux状态变化触发导航:
首先修改Thunk,移除导航相关代码,只处理API请求:
export const loginAdminUser = createAsyncThunk('post/login', async (user: UserLoginProps) => { try { const response = await axios.post(`${environment.BASE_URL}/user/admin-login`, user); console.log('response', response); if (response.data.success) { toast.success(response.data.message); toast('Redirecting to Home page'); localStorage.setItem('token', response.data.data); } else { toast.error(response.data.message); // 抛出错误让Thunk进入rejected状态 throw new Error(response.data.message); } return response.data; } catch (error: unknown) { const err = error as AxiosError; throw new Error(err.message); } });
然后在组件中监听Thunk的执行状态,完成后导航:
import { useDispatch, useSelector } from 'react-redux'; import { useNavigate, useEffect } from 'react-router-dom'; import { loginAdminUser } from '../path/to/thunk'; import type { RootState, UserLoginProps } from '../types'; function LoginComponent() { const dispatch = useDispatch(); const navigate = useNavigate(); // 假设你的Login Slice中有status字段记录请求状态(fulfilled/rejected/pending) const loginStatus = useSelector((state: RootState) => state.login.status); useEffect(() => { // 登录请求成功完成时执行导航 if (loginStatus === 'fulfilled') { navigate('/admin/home'); } }, [loginStatus, navigate]); const handleLogin = (userData: UserLoginProps) => { dispatch(loginAdminUser(userData)); }; // 组件渲染逻辑... }
方式三:用Redux Listener Middleware监听Action(推荐)
这种方式更符合Redux架构,把导航逻辑和组件、Thunk解耦,通过监听Thunk的成功Action触发导航:
- 创建并配置Listener Middleware:
import { configureStore, createListenerMiddleware, isAnyOf } from '@reduxjs/toolkit'; import { loginAdminUser } from '../path/to/thunk'; import { createBrowserHistory } from 'history'; import { loginReducer } from '../slices/loginSlice'; // 创建history实例用于导航 export const history = createBrowserHistory(); const listenerMiddleware = createListenerMiddleware(); // 监听loginAdminUser的成功Action listenerMiddleware.startListening({ matcher: isAnyOf(loginAdminUser.fulfilled), effect: () => { history.push('/admin/home'); }, }); export const store = configureStore({ reducer: { login: loginReducer, // 其他reducers... }, middleware: (getDefaultMiddleware) => getDefaultMiddleware().prepend(listenerMiddleware.middleware), });
- 修改React Router的Router组件,使用自定义的history实例:
import { Router } from 'react-router-dom'; import { history } from './store'; import { Provider } from 'react-redux'; import { store } from './store'; function App() { return ( <Provider store={store}> <Router history={history}> {/* 你的路由配置 */} </Router> </Provider> ); }
- 最后修改Thunk,移除导航代码(和方式二的Thunk代码一致即可)。
内容的提问来源于stack exchange,提问作者3gwebtrain
相关产品推荐
相关产品推荐

