React Redux createAsyncThunk Action无法更新状态问题求助
问题:Redux状态未更新,尽管API返回了用户数据
我正在为Node.js API实现注册页面,使用createAsyncThunk实现获取用户数据并更新Redux状态的功能。API运行正常,派发action时确实会返回创建的用户数据,但用户状态并未更新,始终保持初始状态。
控制台截图

相关代码
User Slice 代码
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit' import axios from 'axios' const initialState = { username: null, email: null, password: null, gamesPlayed: null, gamesWon: null, gamesLost: null, draws: null, loading: false, error: null } const serializeAndStringify = obj => { const seen = [] JSON.stringify( obj, (key, val) => { if(val != null && typeof val === 'object') { if(seen.indexOf(val) >= 0) return seen.push(val) } return val } ) return seen[0] } export const fetchUserData = (requestUrl, userData) => createAsyncThunk( 'userAuth', async () => axios.post(requestUrl, serializeAndStringify(userData)) ) const userSlice = createSlice({ name: 'user', initialState, reducers: { victory (state) { state.gamesPlayed++ state.gamesWon++ }, defeat (state) { state.gamesPlayed++ state.gamesLost++ }, draw (state) { state.gamesPlayed++ state.draws++ } }, extraReducers: { [fetchUserData.pending]: (state, action) => { state.loading = true }, [fetchUserData.fullFilled]: (state, { payload }) => { state.loading = false state.username = payload.data.username state.email = payload.data.email state.password = payload.data.password }, [fetchUserData.rejected]: (state, action) => { state.loading = false state.error = action.payload } } }) export default userSlice.reducer
store.js 代码
import { configureStore } from '@reduxjs/toolkit' import logger from 'redux-logger' import thunk from 'redux-thunk' import userReducer from './user' const reducer = { user: userReducer } const middlewares = [thunk] if(process.env.NODE_ENV === 'development') middlewares.push(logger) const store = configureStore({ reducer, middleware: middlewares, devTools: process.env.NODE_ENV !== 'production', }) export default store
注册页面代码
import { Fragment } from 'react' import { useNavigate } from 'react-router' import { useDispatch } from 'react-redux' import { fetchUserData } from '../../redux/user.js' import './register.styles.scss' const RegisterPage = () => { const navigate = useNavigate() const dispatch = useDispatch() const handleSubmit = event => { event.preventDefault() const { username, email, password } = event.target let userData let requestUrl = '/auth/' if(username) { userData = { username: username.value, email: email.value, password: password.value } requestUrl += 'registration' } else { userData = { email: email.value, password: password.value } requestUrl += 'login' } dispatch(fetchUserData(requestUrl, userData)()) } return ( <Fragment> <div className='register-page'> <form className='container' onSubmit={handleSubmit}> <span>WELCOME BACK</span> <input name='email' required type='email' placeholder='Email' /> <input name='password' required type='password' minLength='8' placeholder='Password' /> <div className='button-wrapper'> <button type='submit'>LOG IN</button> <button className='google-button'> <a href='http://localhost:4000/auth/google'> LOG IN WITH GOOGLE </a> </button> </div> </form> <form className='container' onSubmit={handleSubmit}> <span>REGISTER</span> <input name='username' required type='text' placeholder='Username' /> <input name='email' required type='email' placeholder='Email' /> <input name='password' required type='password' minLength='8' placeholder='Password' /> <div className='button-wrapper'> <button type='submit'>SIGN IN</button> <button className='google-button'> <a href='http://localhost:4000/auth/google'> SIGN IN WITH GOOGLE </a> </button> </div> </form> </div> <button onClick={() => navigate('/', {replace: true})}>Go back</button> </Fragment> ) } export default RegisterPage
问题原因及修复方案
1. createAsyncThunk 创建方式错误
当前fetchUserData是一个返回新createAsyncThunk实例的函数,每次调用都会生成全新的action类型,导致Redux无法匹配extraReducers中定义的处理逻辑。
修复:
直接创建createAsyncThunk,将请求参数作为payload传入:
export const fetchUserData = createAsyncThunk( 'user/userAuth', // 加上slice前缀避免命名冲突 async ({ requestUrl, userData }) => { const serializedData = serializeAndStringify(userData); // 必须await请求获取响应数据 const response = await axios.post(requestUrl, serializedData); return response.data; // 返回后端用户数据,后续payload直接取该值 } );
2. extraReducers 拼写错误
fullFilled应为fulfilled(少一个字母l),导致成功的action无法被捕获处理。
修复:
extraReducers: { [fetchUserData.pending]: (state) => { state.loading = true; }, // 修正拼写为fulfilled [fetchUserData.fulfilled]: (state, { payload }) => { state.loading = false; // payload直接是response.data,无需再取payload.data state.username = payload.username; state.email = payload.email; state.password = payload.password; }, [fetchUserData.rejected]: (state, action) => { state.loading = false; state.error = action.error.message; // rejected时错误信息在action.error中 } }
3. 组件中dispatch调用错误
当前写法dispatch(fetchUserData(requestUrl, userData)())会每次创建新的thunk实例,无法匹配之前定义的extraReducers。
修复:
直接dispatch thunk并传入参数对象:
dispatch(fetchUserData({ requestUrl, userData }));
4. 可选优化:移除冗余thunk中间件
@reduxjs/toolkit的configureStore已默认包含thunk中间件,无需手动引入添加:
// store.js 简化后 import { configureStore } from '@reduxjs/toolkit' import logger from 'redux-logger' import userReducer from './user' const reducer = { user: userReducer } const middlewares = [] if(process.env.NODE_ENV === 'development') middlewares.push(logger) const store = configureStore({ reducer, middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(middlewares), devTools: process.env.NODE_ENV !== 'production', }) export default store
5. 可选:修正serializeAndStringify函数
当前函数逻辑存在问题,JSON.stringify结果未被使用,若用于处理循环引用可简化为:
const serializeAndStringify = obj => { const seen = new WeakSet(); return JSON.parse(JSON.stringify(obj, (key, val) => { if (typeof val === 'object' && val !== null) { if (seen.has(val)) return; seen.add(val); } return val; })); }
若无循环引用问题,可直接传入userData,无需序列化函数。
内容的提问来源于stack exchange,提问作者Saverio Randazzo
相关产品推荐
相关产品推荐

