Redux Persist - 无法读取 属性 of null(读取 'user')
Redux Persist - cannot read property of null (reading 'user')
大家好,我正在尝试使用 MERN 构建一个电子商务项目,但我在使用 redux-persist
时遇到了一些困难。
在 App.jsx
中 useSelector
没有从状态
中读取 user
Property 'user' does not exist on type 'DefaultRootState'
此外,在 requestMethod
中,TOKEN 没有读取 user
,它说“无法读取 属性 of null (“user”)。在使用 redux-persist
之前,注册和登录有效。
我发布了我认为存在一些错误的代码,但如果您认为问题来自其他地方,我也可以添加这些文件。谢谢!
Package.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"
},
Index.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}` },
});
用户 Redux
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;
您的实施存在一些小问题。让我们回顾一下:
首先,您使用选择器从状态中获取 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
,所以三元结果的左侧将是 returned,但在第二个 console.log
中,右侧将是 return ]ed 因为 myAnotherObject
没有被定义和评估为 undefined
.
解决方案
不是将 user
对象评估为 return 正确的路由,而是检查用户 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 ...
令牌
您正在从持久存储中获取令牌,这是不正确的。
而不是从持久存储中检索令牌,直接将其保存在本地存储中,然后在需要时获取它。
// set token
localStorage.setItem('ACCESS_TOKEN', token);
// get token
const myToken = localStorage.getItem('ACCESS_TOKEN)
大家好,我正在尝试使用 MERN 构建一个电子商务项目,但我在使用 redux-persist
时遇到了一些困难。
在 App.jsx
中 useSelector
没有从状态
user
Property 'user' does not exist on type 'DefaultRootState'
此外,在 requestMethod
中,TOKEN 没有读取 user
,它说“无法读取 属性 of null (“user”)。在使用 redux-persist
之前,注册和登录有效。
我发布了我认为存在一些错误的代码,但如果您认为问题来自其他地方,我也可以添加这些文件。谢谢!
Package.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"
},
Index.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}` },
});
用户 Redux
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;
您的实施存在一些小问题。让我们回顾一下:
首先,您使用选择器从状态中获取 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
,所以三元结果的左侧将是 returned,但在第二个 console.log
中,右侧将是 return ]ed 因为 myAnotherObject
没有被定义和评估为 undefined
.
解决方案
不是将 user
对象评估为 return 正确的路由,而是检查用户 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 ...
令牌
您正在从持久存储中获取令牌,这是不正确的。 而不是从持久存储中检索令牌,直接将其保存在本地存储中,然后在需要时获取它。
// set token
localStorage.setItem('ACCESS_TOKEN', token);
// get token
const myToken = localStorage.getItem('ACCESS_TOKEN)