You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无法从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;
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; // 直接返回原状态,不再包裹
  }
};

验证修复效果

  1. 重启项目后,打开Redux DevTools,查看loginUserReducer的初始状态,应该已经包含currentUser字段
  2. NavBar组件会自动读取currentUser并显示用户下拉菜单,而不是登录/注册按钮
  3. Login页面的useEffect会检测到localStorage中的数据,自动跳转到首页

内容的提问来源于stack exchange,提问作者user20872939

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 15:10:06