使用 redux 工具包进行本地存储
Local storage using redux toolkit
我想将我的 isAuthenticated 状态保存在本地存储中,因此在刷新页面后,用户将登录。我尝试直接在 localStorage 中设置它 true/false 并设置初始值我在 redux 中的状态为这个值,但它总是将它设置为 true。
这是我的 redux 商店
import { createSlice, configureStore } from '@reduxjs/toolkit';
//MOVEMENTS (doesn't work yet)
const initialMovementsState = {
movements: [],
};
const movementsSlice = createSlice({
name: 'movements',
initialState: initialMovementsState,
reducers: {
add(state) {
//nothing yet
},
decrement(state) {
//nothing yet
},
},
});
//LOGGING IN/OUT
const initialAuthState = {
isAuthenticated: false,
};
const authSlice = createSlice({
name: 'auth',
initialState: initialAuthState,
reducers: {
login(state) {
state.isAuthenticated = true;
},
logout(state) {
state.isAuthenticated = false;
},
},
});
//STORE CONFIGURATION
const store = configureStore({
reducer: {
movements: movementsSlice.reducer,
auth: authSlice.reducer,
},
});
export const movementsActions = movementsSlice.actions;
export const authActions = authSlice.actions;
export default store;
我找到的所有答案都只针对 redux,而不是 redux 工具包,而且我对 redux 还比较陌生,所以我迷路了。
您必须 dispatch
login
或 logout
操作才能真正改变您在 redux 存储中的状态!
更改 localStorage
是一种副作用,因此您不想在减速器中进行更改。减速器应该始终没有副作用。处理此问题的一种方法是使用自定义中间件。
编写中间件
我们的中间件在每个动作被调度后被调用。如果操作是 login
或 logout
,那么我们将更改 localStorage
值。否则我们什么都不做。无论哪种方式,我们都会使用 return next(action)
.
将操作传递给链中的下一个中间件
redux-toolkit 和 vanilla redux 中间件的唯一区别是我们如何检测 login
和 logout
操作。使用 redux-toolkit,action creator 函数包括一个有用的 match()
函数,我们可以使用它而不必查看 type
。我们知道,如果 login.match(action)
为真,则 action
是一个登录操作。所以我们的中间件可能看起来像这样:
const authMiddleware = (store) => (next) => (action) => {
if (authActions.login.match(action)) {
// Note: localStorage expects a string
localStorage.setItem('isAuthenticated', 'true');
} else if (authActions.logout.match(action)) {
localStorage.setItem('isAuthenticated', 'false');
}
return next(action);
};
应用中间件
您将在 configureStore
函数中将中间件添加到您的商店。 Redux-toolkit includes some middleware by default 启用 thunk、不变性检查和可序列化检查。现在您根本没有在您的商店中设置 middleware
属性,因此您将获得所有默认设置。我们希望确保在添加自定义中间件时保留默认值。
middleware
属性 可以定义为一个函数,它被 redux-toolkit getDefaultMiddleware
函数调用。这允许您为默认中间件设置选项,如果您愿意,同时也可以添加我们自己的。我们将follow the docs举例并写成:
const store = configureStore({
reducer: {
movements: movementsSlice.reducer,
auth: authSlice.reducer,
},
// Note: you can include options in the argument of the getDefaultMiddleware function call.
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(authMiddleware)
});
不要这样做,因为它会删除所有默认中间件
const store = configureStore({
reducer: {
movements: movementsSlice.reducer,
auth: authSlice.reducer,
},
middleware: [authMiddleware]
});
通过中间件同步状态
我们可以通过匹配 all auth
操作来简化我们的中间件。我们通过使用 String.prototype.startsWith()
method on the action.type
(similar to the examples in the addMatcher
docs section 来做到这一点,它使用 .endswith()
).
这里我们通过执行next(action)
找到下一个状态,然后我们改变localStorage
。我们将 localStorage
值设置为 auth
切片返回的新状态。
const authMiddleware = (store) => (next) => (action) => {
const result = next(action);
if ( action.type?.startsWith('auth/') ) {
const authState = store.getState().auth;
localStorage.setItem('auth', JSON.stringify(authState))
}
return result;
};
或者您可以使用 redux-persist 包,它会为您完成。
与此同时,我已经编写了移动逻辑,并希望将我的所有状态保存在本地存储中。 Linda Paiste 的回答非常有帮助(如此长而直截了当的回答值得称赞!),但我正在努力将我的本地存储发送回我的 redux 状态。这是有效的解决方案:
import { createSlice, configureStore } from '@reduxjs/toolkit';
import dummyItems from '../helpers/dummyItems';
const initialMovementsState = {
movements: dummyItems,
};
const movementsSlice = createSlice({
name: 'movements',
initialState: initialMovementsState,
reducers: {
add(state, action) {
state.movements = [action.payload, ...state.movements];
},
delete(state, action) {
const id = action.payload;
state.movements = state.movements.filter(mov => mov.id !== id);
},
},
});
//AUTHORIZATION
const initialAuthState = {
isAuthenticated: false,
};
const authSlice = createSlice({
name: 'auth',
initialState: initialAuthState,
reducers: {
login(state) {
state.isAuthenticated = true;
},
logout(state) {
state.isAuthenticated = false;
},
},
});
//MIDDLEWARE
const localStorageMiddleware = ({ getState }) => {
return next => action => {
const result = next(action);
localStorage.setItem('applicationState', JSON.stringify(getState()));
return result;
};
};
const reHydrateStore = () => {
if (localStorage.getItem('applicationState') !== null) {
return JSON.parse(localStorage.getItem('applicationState')); // re-hydrate the store
}
};
//STORE CONFIGURATION
const store = configureStore({
reducer: {
movements: movementsSlice.reducer,
auth: authSlice.reducer,
},
preloadedState: reHydrateStore(),
middleware: getDefaultMiddleware =>
getDefaultMiddleware().concat(localStorageMiddleware),
});
export const movementsActions = movementsSlice.actions;
export const authActions = authSlice.actions;
export default store;
呢
和githubissue discussion
我想将我的 isAuthenticated 状态保存在本地存储中,因此在刷新页面后,用户将登录。我尝试直接在 localStorage 中设置它 true/false 并设置初始值我在 redux 中的状态为这个值,但它总是将它设置为 true。
这是我的 redux 商店
import { createSlice, configureStore } from '@reduxjs/toolkit';
//MOVEMENTS (doesn't work yet)
const initialMovementsState = {
movements: [],
};
const movementsSlice = createSlice({
name: 'movements',
initialState: initialMovementsState,
reducers: {
add(state) {
//nothing yet
},
decrement(state) {
//nothing yet
},
},
});
//LOGGING IN/OUT
const initialAuthState = {
isAuthenticated: false,
};
const authSlice = createSlice({
name: 'auth',
initialState: initialAuthState,
reducers: {
login(state) {
state.isAuthenticated = true;
},
logout(state) {
state.isAuthenticated = false;
},
},
});
//STORE CONFIGURATION
const store = configureStore({
reducer: {
movements: movementsSlice.reducer,
auth: authSlice.reducer,
},
});
export const movementsActions = movementsSlice.actions;
export const authActions = authSlice.actions;
export default store;
我找到的所有答案都只针对 redux,而不是 redux 工具包,而且我对 redux 还比较陌生,所以我迷路了。
您必须 dispatch
login
或 logout
操作才能真正改变您在 redux 存储中的状态!
更改 localStorage
是一种副作用,因此您不想在减速器中进行更改。减速器应该始终没有副作用。处理此问题的一种方法是使用自定义中间件。
编写中间件
我们的中间件在每个动作被调度后被调用。如果操作是 login
或 logout
,那么我们将更改 localStorage
值。否则我们什么都不做。无论哪种方式,我们都会使用 return next(action)
.
redux-toolkit 和 vanilla redux 中间件的唯一区别是我们如何检测 login
和 logout
操作。使用 redux-toolkit,action creator 函数包括一个有用的 match()
函数,我们可以使用它而不必查看 type
。我们知道,如果 login.match(action)
为真,则 action
是一个登录操作。所以我们的中间件可能看起来像这样:
const authMiddleware = (store) => (next) => (action) => {
if (authActions.login.match(action)) {
// Note: localStorage expects a string
localStorage.setItem('isAuthenticated', 'true');
} else if (authActions.logout.match(action)) {
localStorage.setItem('isAuthenticated', 'false');
}
return next(action);
};
应用中间件
您将在 configureStore
函数中将中间件添加到您的商店。 Redux-toolkit includes some middleware by default 启用 thunk、不变性检查和可序列化检查。现在您根本没有在您的商店中设置 middleware
属性,因此您将获得所有默认设置。我们希望确保在添加自定义中间件时保留默认值。
middleware
属性 可以定义为一个函数,它被 redux-toolkit getDefaultMiddleware
函数调用。这允许您为默认中间件设置选项,如果您愿意,同时也可以添加我们自己的。我们将follow the docs举例并写成:
const store = configureStore({
reducer: {
movements: movementsSlice.reducer,
auth: authSlice.reducer,
},
// Note: you can include options in the argument of the getDefaultMiddleware function call.
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(authMiddleware)
});
不要这样做,因为它会删除所有默认中间件
const store = configureStore({
reducer: {
movements: movementsSlice.reducer,
auth: authSlice.reducer,
},
middleware: [authMiddleware]
});
通过中间件同步状态
我们可以通过匹配 all auth
操作来简化我们的中间件。我们通过使用 String.prototype.startsWith()
method on the action.type
(similar to the examples in the addMatcher
docs section 来做到这一点,它使用 .endswith()
).
这里我们通过执行next(action)
找到下一个状态,然后我们改变localStorage
。我们将 localStorage
值设置为 auth
切片返回的新状态。
const authMiddleware = (store) => (next) => (action) => {
const result = next(action);
if ( action.type?.startsWith('auth/') ) {
const authState = store.getState().auth;
localStorage.setItem('auth', JSON.stringify(authState))
}
return result;
};
或者您可以使用 redux-persist 包,它会为您完成。
与此同时,我已经编写了移动逻辑,并希望将我的所有状态保存在本地存储中。 Linda Paiste 的回答非常有帮助(如此长而直截了当的回答值得称赞!),但我正在努力将我的本地存储发送回我的 redux 状态。这是有效的解决方案:
import { createSlice, configureStore } from '@reduxjs/toolkit';
import dummyItems from '../helpers/dummyItems';
const initialMovementsState = {
movements: dummyItems,
};
const movementsSlice = createSlice({
name: 'movements',
initialState: initialMovementsState,
reducers: {
add(state, action) {
state.movements = [action.payload, ...state.movements];
},
delete(state, action) {
const id = action.payload;
state.movements = state.movements.filter(mov => mov.id !== id);
},
},
});
//AUTHORIZATION
const initialAuthState = {
isAuthenticated: false,
};
const authSlice = createSlice({
name: 'auth',
initialState: initialAuthState,
reducers: {
login(state) {
state.isAuthenticated = true;
},
logout(state) {
state.isAuthenticated = false;
},
},
});
//MIDDLEWARE
const localStorageMiddleware = ({ getState }) => {
return next => action => {
const result = next(action);
localStorage.setItem('applicationState', JSON.stringify(getState()));
return result;
};
};
const reHydrateStore = () => {
if (localStorage.getItem('applicationState') !== null) {
return JSON.parse(localStorage.getItem('applicationState')); // re-hydrate the store
}
};
//STORE CONFIGURATION
const store = configureStore({
reducer: {
movements: movementsSlice.reducer,
auth: authSlice.reducer,
},
preloadedState: reHydrateStore(),
middleware: getDefaultMiddleware =>
getDefaultMiddleware().concat(localStorageMiddleware),
});
export const movementsActions = movementsSlice.actions;
export const authActions = authSlice.actions;
export default store;
和githubissue discussion