react | redux | redux-persist | 状态管理 | 持久化存储

624 阅读4分钟

redux官网:redux.js.org/

image.png

使用

安装

Redux Toolkit 包含了 Redux 核心,以及我们认为对于构建 Redux 应用程序必不可少的其他关键软件包(例如 Redux Thunk 和 Reselect)。

npm install @reduxjs/toolkit
npm install react-redux
npm install --save-dev redux-devtools

配置

在根目录下创建features文件夹,比如存储计数功能,就再创建一个couter文件夹,里面编写计数方法counterSlice文件:

image.png couterSlice.js文件

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

export const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    value: 0,
  },
  reducers: {
    increment: (state) => {
      // Redux Toolkit 允许我们在 reducers 写 "可变" 逻辑。它
      // 并不是真正的改变状态值,因为它使用了 Immer 库
      // 可以检测到“草稿状态“ 的变化并且基于这些变化生产全新的
      // 不可变的状态
      state.value += 1;
    },
    decrement: (state) => {
      state.value -= 1;
    },
    incrementByAmount: (state, action) => {
      state.value += action.payload;
    },
  },
});
// 每个 case reducer 函数会生成对应的 Action creators
export const { increment, decrement, incrementByAmount } = counterSlice.actions;

export default counterSlice.reducer;

在根目录下创建app文件夹,里面再创建一个store的js文件,导入刚刚的couter桶。

image.png store.js文件

import { configureStore } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';

export default configureStore({
  reducer: {
    // (createSlice的name) :  (name + createSlice的export default .点后面)
    counter: counterReducer,
  },
});

在couter文件夹下创建couter文件,在里面用useSelector获取couter桶内数据。

couter.js文件(具体情况具体创建使用)

// redux_demo
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { decrement, increment } from './counterSlice';
// import styles from './Counter.module.css';

export function Counter() {
    //useSelector()钩子获取桶内数据
  const count = useSelector((state) => state.counter.value);
  const dispatch = useDispatch();

  return (
    <div style={{ position: 'absolute', top: '50%', left: '50%' }}>
      <div>
        <button
          aria-label="Increment value"
          onClick={() => dispatch(increment())}
        >
          Increment
        </button>
        <span>{count}</span>
        <button
          aria-label="Decrement value"
          onClick={() => dispatch(decrement())}
        >
          Decrement
        </button>
      </div>
    </div>
  );
}

因为 Redux 的数据存储是通过使用一个全局的状态树来管理应用程序的状态。 这个状态树是一个不可变的对象,通过 Redux 提供的函数来修改。所有的组件都可以从状态树中获取数据,并目当状态发生改变时,组件会自动更新。Redux的数据存储是在内存中进行的,数据的读取和写入速度非常快。相比之下,本地存储是指将数据存储在浏览器的本地存储中,比如使用 localStorage或sessionStorage¹。Redux 的核心是一个store 对象,它里面包含着我们所储存的所有状态,类似一个物流中心,我们可以往里面存放数据,也可以从里面取出数据。Redux桶内数据刷新后就没有了。 所以我们需要对它做一个持久化存储。

redux-persist官网:redux-toolkit.js.org/rtk-query/u…

github:github.com/rt2zz/redux…

image.png

安装

npm install redux-persist

使用

针对多个reducer持久化存储方案:

根目录下的app文件夹里面的store文件里:

import { configureStore } from '@reduxjs/toolkit';
import storage from 'redux-persist/lib/storage'; // defaults to localStorage for web
import { persistReducer, persistStore } from 'redux-persist';
import { combineReducers } from 'redux';
// 多个reducer,比如:counterReducer、listReducer、listProjectReducer
import counterReducer from '../features/counter/counterSlice';
import listReducer from '../features/dashboard/listSlice';
import listProjectReducer from '../features/projects/listSilce';

//使用 combineReducers 将所有的 reducer 组合起来。
const rootReducer = combineReducers({
  counter: counterReducer,
  list: listReducer,
  listProject: listProjectReducer,
  // 其他的 reducer 可以继续添加
});

// 配置持久化
const persistConfig = {
  key: 'root',
  storage,
  // 你可以使用白名单或黑名单来指定哪些 reducer 需要持久化
  whitelist: ['counter', 'list', 'listProject'], // 只有这两个 reducer 会被持久化
  // blacklist: ['someOtherReducer'], // 不持久化的 reducer
};

// 创建持久化的 reducer
const persistedReducer = persistReducer(persistConfig, rootReducer);

// 创建 Redux store
const store = configureStore({
  reducer: persistedReducer,
});

export const persistor = persistStore(store);
export default store;

tips:

  • 白名单和黑名单:你可以通过 whitelist 和 blacklist 配置选项来决定哪些 reducer 的状态需要持久化。使用白名单时,只持久化白名单中的 reducer;使用黑名单时,不持久化黑名单中的 reducer。
  • 持久化存储redux-persist 默认使用 localStorage。如果你需要使用其他存储(如 sessionStorage),可以在 persistConfig 中更改 storage 配置。

根目录下的index文件里:

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

//在你的应用入口文件中,使用 `PersistGate` 来包裹你的应用,这样在状态恢复之前,UI 不会渲染
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Provider store={store}>
    <PersistGate loading={null} persistor={persistor}>
      <React.StrictMode>
        <App />
      </React.StrictMode>
    </PersistGate>
  </Provider>
);

现在,就可以在localstorage里面查看存储的数据:

image.png

这时可能会有一个报错store.js:50 A non-serializable value was detected in an action, in the path: register. Value: ƒ register(key) { _pStore.dispatch({ type: _constants__WEBPACK_IMPORTED_MODULE_0__.REGISTER, key: key }); }  Take a look at the logic that dispatched this action: {type: 'persist/PERSIST', register: ƒ, rehydrate: ƒ}……

它表示这是非序列化的数据,所以报错。 这时你可以在persistConfig里面添加以下代码以关闭redux序列化检测:

 middleware: (getDefaultMiddleware) =>
   getDefaultMiddleware({
     serializableCheck: false,
 }),