Redux报错Actions must be plain objects无法存储用户数据求助
问题根因
你遇到的Actions must be plain objects. Use custom middleware for async actions报错由3个核心原因导致:
- 命名冲突:登录组件中自定义的异步登录请求函数与从userSlice导入的
loginaction重名,导致dispatch(login())实际传入的是异步函数而非Redux要求的普通对象类型action,直接触发报错。 - 异步时序错误:你尝试先更新React本地状态再同步到Redux,但React状态更新是异步的,
setState执行后立刻dispatch拿到的还是旧值,同步到Redux的数据始终是初始值。 - 数据结构嵌套错误:传给login action的参数每个字段额外套了一层对象,和slice定义的state结构不匹配,就算数据存进去也无法正确读取。
代码修改方案
1. features/user.js 补全导入(你原有代码漏了createSlice导入,先补上:
import { createSlice } from '@reduxjs/toolkit' export const userSlice = createSlice({ name: "user", initialState: { value: { name: "", id: 0, email: "", loginId: "" }, }, reducers: { login: (state, action) => { state.value = action.payload; }, }, }); export const { login } = userSlice.actions; export default userSlice.reducer;
2. login_component.js 核心修改:
- 重命名本地登录请求函数避免和导入的login action重名,请求成功后直接dispatch,不需要先存本地状态避免异步时序问题:
import React, { useState, useEffect } from "react"; import axios from "axios"; import { useDispatch } from "react-redux"; import { login } from "../features/user"; function Login() { const [LoginID, setLoginID] = useState(""); const [Password, setPassword] = useState(""); const [LoginStatus, setLoginStatus] = useState(""); axios.defaults.withCredentials = true; const dispatch = useDispatch(); // 重命名本地登录请求函数,避免和导入的login action重名 const handleLoginRequest = () => { axios .post("http://localhost:8000/login", { LoginID: LoginID, Password: Password, }) .then((response) => { if (response.data.message) { setLoginStatus(response.data.message); } else { const userInfo = response.data.result[0]; setLoginStatus(userInfo.UserFullName); // 请求成功后直接同步到Redux,不需要先存本地状态 dispatch(login({ name: userInfo.UserFullName, id: userInfo.UserID, email: userInfo.UserEmailID, loginId: userInfo.LoginID })) } }); }; const handleSubmit = (e) => { e.preventDefault(); handleLoginRequest(); }; useEffect(() => { axios.get("http://localhost:8000/login").then((response) => { if (response.data.loggedIn === true) { const userInfo = response.data.user[0]; setLoginStatus(userInfo.UserFullName); // 页面刷新后如果已经登录,同步用户信息同步到Redux dispatch(login({ name: userInfo.UserFullName, id: userInfo.UserID, email: userInfo.UserEmailID, loginId: userInfo.LoginID })) } }); }, [dispatch]); return ( <div className="App"> <div className="auth-wrapper"> <div className="auth-inner"> <form onSubmit={handleSubmit}> <h3>Log In</h3> <div className="form-group "> <label>Login ID</label> <input type="text" className="form-control" placeholder="Login ID" onChange={(e) => setLoginID(e.target.value)} /> </div> <div className="form-group"> <label>Password</label> <input type="password" className="form-control" placeholder="Password" onChange={(e) => setPassword(e.target.value)} /> </div> <button className="btn btn-primary btn-block" type="submit"> Login </button> </form> <h1>{LoginStatus}</h1> </div> </div> </div> ); } export default Login;
3. nav_component.js 冗余导入和显示逻辑修改:
删除无用的user导入,把class属性改为React规范的className,调整显示字段为用户名:
import { Link } from "react-router-dom"; import { useSelector } from "react-redux"; function Nav(props) { const user = useSelector((state) => state.user.value); return ( <nav className="navbar navbar-expand navbar-light fixed-top"> <ul className="nav"> <li className="nav-item"> <Link to={"/"} className="nav-link active" aria-current="page"> Home </Link> </li> <li className="nav-item"> <Link to={"/register"} className="nav-link"> Register </Link> </li> <li className="nav-item"> <Link to={"/login"} className="nav-link"> Login </Link> </li> <li className="nav-item"> <Link to={"/admin"} className="nav-link" > Admin </Link> </li> </ul> <ul className="username"> <li>Welcome {user.name}</li> </ul> </nav> ); } export default Nav;
内容的提问来源于stack exchange,提问作者Chirag Maurya
相关产品推荐
相关产品推荐

