使用redux工具包进行本地存储

Ani*_*ska 17 local-storage reactjs redux redux-toolkit

我想将 isAuthenticated 状态保留在本地存储中,因此刷新页面后,用户将登录。我尝试直接在 localStorage 中将其设置为 true/false 并将 redux 中状态的初始值设置为此value,但它总是将其设置为 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;
Run Code Online (Sandbox Code Playgroud)

我找到的所有答案都仅与 redux 相关,而不是与 redux 工具包相关,而且我对 redux 还很陌生,所以我迷路了。

Lin*_*ste 44

2022 年 10 月更新createListenerMiddleware:您还可以在 1.8 及更高版本中使用 redux-toolkit ,如本答案中所述


更改localStorage是一种副作用,因此您不想在您的减速器中进行更改。减速器应该始终没有副作用。处理此问题的一种方法是使用自定义中间件。

编写中间件

我们的中间件在每个操作被调度后都会被调用。如果操作是loginlogout那么我们将更改该localStorage值。否则我们什么也不做。无论哪种方式,我们都会将操作传递给链中的下一个中间件return next(action)

redux-toolkit 和 vanilla redux 中间件的唯一区别是我们如何检测loginlogout操作。使用 redux-toolkit,动作创建器功能包括一个有用的match()功能,我们可以使用它,而不必查看type. 我们知道,action如果为 true,则 an 是登录操作login.match(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);
};
Run Code Online (Sandbox Code Playgroud)

应用中间件

您将在函数中将中间件添加到您的商店configureStore。Redux-toolkit默认包含一些中间件,支持 thunk、不变性检查和可序列化检查。现在,您middleware根本没有在商店中设置该属性,因此您将获得所有默认值。我们希望确保在添加自定义中间件时保留默认值。

middleware属性可以定义为一个由 redux-toolkit 函数调用的函数getDefaultMiddleware。如果您愿意,这允许您设置默认中间件的选项,同时还可以添加我们自己的中间件。我们将按照文档示例编写:

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)
});
Run Code Online (Sandbox Code Playgroud)

不要这样做,因为它将删除所有默认中间件

const store = configureStore({
  reducer: {
    movements: movementsSlice.reducer,
    auth: authSlice.reducer,
  },
  middleware: [authMiddleware]
});
Run Code Online (Sandbox Code Playgroud)

通过中间件同步状态

我们可以通过匹配所有 auth操作来简化我们的中间件。String.prototype.startsWith()我们通过使用上的方法来做到这一点(类似于文档部分中使用 的action.type示例)。addMatcher.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;
};
Run Code Online (Sandbox Code Playgroud)

或者您可以使用redux-persist包,它会为您完成此操作。


Ani*_*ska 19

与此同时,我已经编写了运动逻辑,并希望将所有状态保留在本地存储中。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;

Run Code Online (Sandbox Code Playgroud)