React+Redux Toolkit注册成功后如何实现定向跳转?
问题背景
我正在开发一个包含注册与授权逻辑的小型应用,已实现注册功能:注册成功时会正确更新所有状态,失败时会将isError设为true并获取错误文本。目前的问题是,我希望仅在注册成功时才将用户重定向到其他页面。
技术栈:React + TypeScript + Redux Toolkit(含createAsyncThunk)+ react-router-dom + react-hook-form + Zod
注册组件代码
import clsx from 'clsx' import { FC } from 'react' import { useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { useNavigate } from 'react-router-dom' import { useAppDispatch, useAppSelector } from '../../store/hooks' import { registerUser } from '../../store/slices/userSlice' import { FormInput } from '../../components/FormInput/FormInput' import { Button } from '@gravity-ui/uikit' import { registerSchema, TRegisterSchema } from '../../models/AuthSchema' import styles from './RegisterForm.module.sass' interface AuthFormProps { className?: string } export const RegisterForm: FC<AuthFormProps> = ({ className }) => { const dispatch = useAppDispatch() const { errorText } = useAppSelector((state) => state.userSlice) const navigate = useNavigate() const { register, handleSubmit, formState: { errors }, reset, } = useForm<TRegisterSchema>({ resolver: zodResolver(registerSchema), }) const onSubmit = async (data: TRegisterSchema) => { dispatch(registerUser(data)) reset() } return ( <div className={clsx(className, styles.wrapper)}> <form className={styles.form} onSubmit={handleSubmit(onSubmit)}> <FormInput title='Email' register={register} registerName='email' errorText={errors.email?.message} isErorr={!!errors.email} /> <FormInput title='Name' register={register} registerName='name' errorText={errors.name?.message} isErorr={!!errors.name} /> <FormInput title='Password' register={register} registerName='password' errorText={errors.password?.message} isErorr={!!errors.password} /> <Button type='submit' className={styles.button} size='xl'> Register </Button> </form> </div> ) }
Redux Slice代码(含createAsyncThunk)
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' import UserService from '../../api/Services/UserService' import { AxiosError } from 'axios' interface IInitialState { user: { accessToken: string email: string name: string } isError: boolean errorText: string isLoading: boolean } interface IAuthInfo { name: string email: string password: string } export const registerUser = createAsyncThunk( 'user/register', async (data: IAuthInfo, { rejectWithValue }) => { try { const response = await UserService.register( data.email, data.password, data.name ) return response } catch (error: AxiosError | unknown) { if (error instanceof AxiosError) { return rejectWithValue(error.response?.data) } } } ) const initialState: IInitialState = { user: { accessToken: '', email: '', name: '', }, errorText: '', isError: false, isLoading: false, } export const userSlice = createSlice({ name: 'general', initialState, reducers: {}, extraReducers: (builder) => { builder.addCase(registerUser.pending, (state) => { state.isLoading = true state.errorText = '' state.isError = false }) builder.addCase(registerUser.fulfilled, (state, action) => { if (action.payload) { state.user.accessToken = action.payload?.accessToken state.user.email = action.payload.user?.email state.user.name = action.payload.user?.name state.errorText = '' state.isError = false state.isLoading = false localStorage.setItem('token', action.payload?.accessToken) } }) builder.addCase(registerUser.rejected, (state, action) => { state.errorText = action.payload as string state.isError = true state.isLoading = false }) }, }) export default userSlice.reducer
我曾尝试通过Redux的isError状态判断跳转,但isError默认值为false,导致页面一加载就触发跳转,无法满足需求。
解决方案
方法一:利用dispatch返回的Promise(推荐)
createAsyncThunk返回的action被dispatch后,会返回一个Promise,通过unwrap()方法可以直接获取thunk的成功结果或捕获错误。在组件的onSubmit函数里处理成功后的跳转逻辑:
修改注册组件中的onSubmit函数:
const onSubmit = async (data: TRegisterSchema) => { try { // 等待thunk执行完成并获取成功结果 await dispatch(registerUser(data)).unwrap(); reset(); // 注册成功后跳转 navigate('/dashboard'); // 替换为你的目标路径 } catch (err) { // 注册失败时无需额外操作,因为slice已经更新了错误状态 console.error('注册失败:', err); } };
原理:unwrap()会提取thunk成功时返回的值,如果thunk被rejectWithValue拒绝,则会抛出对应的错误,这样我们就能精准区分注册成功/失败的场景,仅在成功时执行跳转。
方法二:监听Redux状态变化
通过useEffect监听注册成功后的状态变化(比如user.accessToken从空变为非空),触发跳转:
在注册组件中添加以下代码:
const { user, isLoading, isError } = useAppSelector((state) => state.userSlice); const navigate = useNavigate(); useEffect(() => { // 仅当accessToken存在、非加载状态、无错误时跳转 if (user.accessToken && !isLoading && !isError) { navigate('/dashboard'); // 可选:跳转后重置表单或状态,避免刷新页面重复跳转 reset(); } }, [user.accessToken, isLoading, isError, navigate, reset]);
注意:如果用户刷新页面,localStorage中的token会保留,可能导致页面再次跳转。可以在slice中添加一个重置状态的reducer,或者在跳转后清除相关状态(根据业务需求调整)。
方法三:不推荐:在Slice中直接跳转
虽然可以在registerUser.fulfilled回调中调用navigate,但不建议这么做——Redux Slice应该保持纯函数,不依赖路由等外部API,否则会降低代码的可维护性和可测试性。
内容的提问来源于stack exchange,提问作者Akaev Dzhalal

