【问题标题】:Redux Persist - cannot read property of null (reading 'user')Redux Persist - 无法读取 null 的属性(读取“用户”)
【发布时间】:2021-12-01 18:19:40
【问题描述】:

大家好,我正在尝试使用 MERN 构建一个电子商务项目,但我在使用 redux-persist 时遇到了一些困难。

App.jsx 中,useSelector 不会从状态中读取user

Property 'user' does not exist on type 'DefaultRootState'

另外,在requestMethod TOKEN 不读取user 中,它说“无法读取null(“用户”)的属性。在使用redux-persist 之前,注册和登录工作。

我发布了我认为存在一些错误的代码,但如果您认为问题来自其他地方,我也可以添加这些文件。谢谢!

包.json

 "dependencies": {
    "@material-ui/core": "^4.12.3",
    "@material-ui/icons": "^4.11.2",
    "@reduxjs/toolkit": "^1.6.2",
    "@testing-library/jest-dom": "^5.14.1",
    "@testing-library/react": "^11.2.7",
    "@testing-library/user-event": "^12.8.3",
    "axios": "^0.21.4",
    "json-server": "^0.16.3",
    "mongodb": "^4.1.3",
    "mongoose": "^6.0.9",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-modal": "^3.14.3",
    "react-redux": "^7.2.5",
    "react-reveal": "^1.2.2",
    "react-router-dom": "^5.3.0",
    "react-scripts": "4.0.3",
    "react-stripe-checkout": "^2.6.3",
    "redux": "^4.1.1",
    "redux-devtools-extension": "^2.13.9",
    "redux-persist": "^6.0.0",
    "redux-thunk": "^2.3.0",
    "reduxjs-toolkit-persist": "^7.0.7",
    "shortid": "^2.2.16",
    "styled-components": "^5.3.1",
    "web-vitals": "^1.1.2"
  },

索引.jsx

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { Provider } from "react-redux";
import { store, persistor } from "./redux/store";
import { PersistGate } from 'redux-persist/integration/react'

ReactDOM.render(
  <Provider store={store}>
    <PersistGate loading={null} persistor={persistor}>
      <App />
    </PersistGate>
  </Provider>,
  document.getElementById("root")
);

App.jsx

const App = () => {
  const user = useSelector((state) => state.user.currentUser);
  return (
    <Router>
      <Switch>
        <Route exact path="/">
          <Home/>
        </Route>
        <Route path="/products/:category">
          <ProductList/>
        </Route>
        <Route path="/product/:id">
          <Product />
        </Route>
        <Route path="/cart">
          <Cart/>
        </Route>
        <Route path="/success">
          <Success/>
        </Route>
        <Route path="/login">
          {user ? <Redirect to ="/"/> : <Login/>}
          <Login/>
        </Route>
        <Route path="/register">
        {user ? <Redirect to ="/"/> : <Register/>}

state.user

Object
cart:
products: []
quantity: 0
total: 0
[[Prototype]]: Object
user:
currentUser: {_id: '616001631a1375942f6d7dd9', username: 'admin', email: 'admin@gmail.com', isAdmin: true, createdAt: '2021-10-08T08:29:23.827Z', …}
error: false
isFetching: false
[[Prototype]]: Object
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
__proto__: (...)
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()
_persist:
rehydrated: true
version: 1
[[Prototype]]: Object
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
__proto__: (...)
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()
[[Prototype]]: Object
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
__proto__: (...)
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()

Store.js

import { configureStore, combineReducers } from "@reduxjs/toolkit";
import cartReducer from "./cartRedux";
import userReducer from "./userRedux";
import {
    persistStore,
    persistReducer,
    FLUSH,
    REHYDRATE,
    PAUSE,
    PERSIST,
    PURGE,
    REGISTER,
} from "redux-persist";
import storage from "redux-persist/lib/storage";


const persistConfig = {
    key: "root",
    version: 1,
    storage,
};

const rootReducer = combineReducers({ user: userReducer, cart: cartReducer })

const persistedReducer = persistReducer(persistConfig, rootReducer);


export const store = () => configureStore({
    reducer:
        persistedReducer,
    middleware: (getDefaultMiddleware) =>
        getDefaultMiddleware({
            serializableCheck: {
                ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
            },
        })
})


export let persistor = persistStore(configureStore);

requestMethods.js

import axios from "axios";

const BASE_URL = "http://localhost:5000/api/";
const TOKEN =
    JSON.parse(JSON.parse(localStorage.getItem('persist:root')).user).currentUser
        .accessToken || "";

export const publicRequest = axios.create({
    baseURL: BASE_URL,
});

export const userRequest = axios.create({
    baseURL: BASE_URL,
    header: { token: `Bearer ${TOKEN}` },
});

用户还原

import { createSlice } from "@reduxjs/toolkit";

const userSlice = createSlice({
    name: "user",
    initialState: {
        currentUser: null,
        isFetching: false,
        error: false,
    },
    reducers: {
        loginStart: (state) => {
            state.isFetching = true;
        },
        loginSuccess: (state, action) => {
            state.isFetching = false;
            state.currentUser = action.payload;
        },
        loginFailure: (state) => {
            state.isFetching = false;
            state.error = true;
        },
    },
});

export const { loginStart, loginSuccess, loginFailure } = userSlice.actions;
export default userSlice.reducer;

【问题讨论】:

  • 你在哪里用 redux 提供者包装你的 App 组件?
  • @novonimo 嗨!问题自行消失了,但后来又回来了,仍然没有解决。我在 redux 提供者所在的位置添加了 index.js。
  • 我在控制台中尝试: fetch("localhost:5000/api/") 并返回 GET 404(未找到)
  • 在您的 App 组件中,const user = useSelector(state =&gt; state) 的结果是什么?请更改选择器并将结果记录在 user 上。我正在寻找你的整个状态对象来证明你的 redux 商店的一切都正常工作。
  • @novonimo 我添加了 const user = useSelector((state) => state); console.log(user) 并在 App.jsx 代码之后发布了上面的日志:)

标签: reactjs redux redux-persist


【解决方案1】:

您的实施存在一些小问题。让我们回顾一下:

首先,您使用选择器从状态中获取user 对象,您的user 变量(在App 组件中)现在是一个对象。

const yourStateObject = {
  user: {
    currentUser: {_id: '616001631a1375942f6d7dd9', username: 'admin', email: 'admin@gmail.com', isAdmin: true, createdAt: '2021-10-08T08:29:23.827Z', ...}
  }
}

现在,看看你的选择器的结果:

const user = useSelector(state => state.user.currentUser)

console.log(user) // {_id: '616001631a1375942f6d7dd9', username: 'admin', email: 'admin@gmail.com', isAdmin: true, createdAt: '2021-10-08T08:29:23.827Z', ...}

问题

到目前为止,一切都很好,但是在 Router 中,您正尝试使用三元运算符检查 user 对象。

考虑这个例子:

const myObject = {}

console.log(myObject ? "Yes this is an object" : "Nothing exist") 

console.log(myAnotherObject ? "Yes this is an object" : "Nothing exist") 

由于定义了myObject,所以会返回三元结果的左侧,但是在第二个console.log中,由于myAnotherObject没有定义和评估为undefined,所以会返回右侧.

解决方案

不是评估 user 对象以返回正确的路由,而是检查用户 ID 或用户名。

const userId = useSelector(state => state.user.currentUser._id)

return (
  // rest of the codes ...
  {userId ? <Redirect to ="/"/> : <Login/>}
  // rest of the codes ...
)

另外,您需要在userSlice 中使用{} 作为initialState 而不是null

const userSlice = createSlice({
    name: "user",
    initialState: {
        currentUser: {},   // ------> here
        isFetching: false,
        error: false,
    },
   // rest of the codes ...

令牌

您正在从持久存储中获取令牌,这是不正确的。 与其从持久化存储中检索令牌,不如直接将其保存在 localStorage 上,然后在需要时获取它。

// set token
localStorage.setItem('ACCESS_TOKEN', token);

// get token
const myToken = localStorage.getItem('ACCESS_TOKEN)

【讨论】:

  • 非常感谢!我会尽快实施并公布结果:)
  • 我很高兴@MariusPetrut。我正在等待您对实施的回复,请随时提出问题。
  • 你是对的,解决了这个问题!非常感谢
猜你喜欢
  • 2022-11-20
  • 2022-01-06
  • 2021-03-08
  • 2019-06-30
  • 1970-01-01
  • 1970-01-01
  • 2021-11-28
  • 2022-01-12
相关资源
最近更新 更多