无法从Redux的loginUserReducer获取数据,请求技术排查
React Redux 状态恢复问题排查
我在React项目中使用Redux管理状态,localStorage里已经保存了登录用户的currentUser数据,但负责登录功能的loginUserReducer无法获取到该数据。具体表现为:
- Redux DevTools中
loginUserReducer的初始状态为空对象 - localStorage中可以看到
currentUser的完整用户数据
相关代码如下:
userReducer.js
//Register Reducer export const registerUserReducer = (state = {}, action) => { switch (action.type) { case "USER_REGISTER_REQUEST": return { loading: true, }; case "USER_REGISTER_SUCCESS": return { loading: false, success: true, }; case "USER_REGISTER_FAIL": return { loading: false, error: action.payload, }; default: return { state }; } }; //Login Reducer export const loginUserReducer = (state = {}, action) => { switch (action.type) { case "USER_LOGIN_REQUEST": return { loading: true, }; case "USER_LOGIN_SUCCESS": return { loading: false, success: true, currentUser: action.payload, }; case "USER_LOGIN_FAIL": return { loading: false, error: action.payload, }; default: return state; } };
store.js
import { configureStore } from "@reduxjs/toolkit"; import { combineReducers, applyMiddleware } from "redux"; import thunk from "redux-thunk"; import { composeWithDevTools } from "redux-devtools-extension"; import { getAllPizzaReducer } from "./reducers/pizzaReducer"; import { cartReducer } from "./reducers/cartReducer"; import { registerUserReducer, loginUserReducer } from "./reducers/userReducer"; //Getting cartItems from local storage const cartItems = localStorage.getItem("cartItems") ? JSON.parse(localStorage.getItem("cartItems")) : []; //Getting current user from local storage const currentUser = localStorage.getItem("currentUser") ? JSON.parse(localStorage.getItem("currentUser")) : null; const reducers = combineReducers({ getAllPizzaReducer: getAllPizzaReducer, cartReducer: cartReducer, registerUserReducer: registerUserReducer, loginUserReducer: loginUserReducer, }); const initialState = { cartReducer: { cartItems: cartItems, }, loginUserReducer: { currentUser: currentUser, }, }; const middleware = [thunk]; const strore = configureStore( { reducer: reducers }, initialState, composeWithDevTools(applyMiddleware(...middleware)) ); export default strore;
Login.jsx
const Login = () => { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const dispatch = useDispatch() const loginState = useSelector((state) => state.loginUserReducer); const { error, success, loading } = loginState; useEffect(() =>{ if(localStorage.getItem("currentUser")){ window.location.href = "/" } },[]) const loginHandler = () => { const user ={email,password} dispatch(loginUser(user)) } return ( <> <Container> {loading && <Loader />} {success && <Success success="User Logged In Successfully" />} {error && <Error error="somthing went wrong" />} <Form> <Form.Group className="mb-3" controlId="formBasicEmail"> <Form.Label>Email address</Form.Label> <Form.Control type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Enter email" /> <Form.Text className="text-muted"> We'll never share your email with anyone else. </Form.Text> </Form.Group> <Form.Group className="mb-3" controlId="formBasicPassword"> <Form.Label>Password</Form.Label> <Form.Control type="password" value={password} onChange={(e) => setPassword(e.target.value)}placeholder="Password" /> </Form.Group> <Form.Group className="mb-3" controlId="formBasicCheckbox"> <Form.Check type="checkbox" label="Check me out" /> </Form.Group> <Button variant="primary" onClick={loginHandler}> Login </Button> </Form> </Container> </> ) } export default Login;
NavBar.jsx
import React from "react"; import { Navbar, Nav, Container, Image, DropdownButton,Dropdown } from "react-bootstrap"; import { useDispatch, useSelector } from "react-redux"; import { LinkContainer } from "react-router-bootstrap"; import { logoutUser } from "../actions/userAction"; const NavBar = () => { const dispatch = useDispatch(); const cartState = useSelector((state) => state.cartReducer); const userState = useSelector((state) => state.loginUserReducer); const { currentUser } = userState; return ( <> <Navbar collapseOnSelect expand="lg" bg="light" variant="light"> <Container> <Navbar.Brand> <Image src="images/logo.png" alt="logo" style={{ height: "50px" }} /> </Navbar.Brand> <Navbar.Toggle aria-controls="responsive-navbar-nav" /> <Navbar.Collapse id="responsive-navbar-nav"> <Nav className="ms-auto"> {currentUser ? ( <LinkContainer to="/"> <DropdownButton id="dropdown-basic-button" title={currentUser.name}> <Dropdown.Item href="#/action-1">Orders</Dropdown.Item> <Dropdown.Item onClick={()=>{dispatch(logoutUser())}}> Logout </Dropdown.Item> </DropdownButton> </LinkContainer> ) : ( <> {" "} <LinkContainer to="/login"> <Nav.Link>Login</Nav.Link> </LinkContainer> <LinkContainer to="/register"> <Nav.Link>Register</Nav.Link> </LinkContainer>{" "} </> )} <LinkContainer to="/cart"> <Nav.Link>Cart {cartState.cartItems.length}</Nav.Link> </LinkContainer> </Nav> </Navbar.Collapse> </Container> </Navbar> </> ); }; export default NavBar;
问题排查与修复
1. 核心问题:configureStore 参数传递错误
@reduxjs/toolkit的configureStore只接受单个配置对象,你之前把initialState和中间件作为独立参数传入,导致预加载状态完全没生效。正确的写法是把preloadedState和middleware放在配置对象内部。
2. 次要问题:registerUserReducer 默认返回错误
registerUserReducer的default分支返回{ state },这会把原状态包裹在state属性里,破坏状态结构,应该直接返回state。
修复后的代码
修正 store.js
import { configureStore } from "@reduxjs/toolkit"; import { combineReducers } from "redux"; import thunk from "redux-thunk"; import { getAllPizzaReducer } from "./reducers/pizzaReducer"; import { cartReducer } from "./reducers/cartReducer"; import { registerUserReducer, loginUserReducer } from "./reducers/userReducer"; // 获取localStorage数据 const cartItems = localStorage.getItem("cartItems") ? JSON.parse(localStorage.getItem("cartItems")) : []; const currentUser = localStorage.getItem("currentUser") ? JSON.parse(localStorage.getItem("currentUser")) : null; const reducers = combineReducers({ getAllPizzaReducer: getAllPizzaReducer, cartReducer: cartReducer, registerUserReducer: registerUserReducer, loginUserReducer: loginUserReducer, }); const initialState = { cartReducer: { cartItems: cartItems, }, loginUserReducer: { currentUser: currentUser, }, }; // 正确的configureStore调用方式 const store = configureStore({ reducer: reducers, preloadedState: initialState, // 集成thunk中间件 middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(thunk), // 开发环境启用DevTools devTools: process.env.NODE_ENV !== 'production' }); export default store;
修正 userReducer.js 中的 registerUserReducer
//Register Reducer export const registerUserReducer = (state = {}, action) => { switch (action.type) { case "USER_REGISTER_REQUEST": return { loading: true, }; case "USER_REGISTER_SUCCESS": return { loading: false, success: true, }; case "USER_REGISTER_FAIL": return { loading: false, error: action.payload, }; default: return state; // 直接返回原状态,不再包裹 } };
验证修复效果
- 重启项目后,打开Redux DevTools,查看
loginUserReducer的初始状态,应该已经包含currentUser字段 - NavBar组件会自动读取
currentUser并显示用户下拉菜单,而不是登录/注册按钮 - Login页面的
useEffect会检测到localStorage中的数据,自动跳转到首页
内容的提问来源于stack exchange,提问作者user20872939
相关产品推荐
相关产品推荐

