likes
comments
collection

如何在React中实现全局数据的状态持久化?一篇文章让你看懂状态持久化

作者站长头像
站长
· 阅读数 11

前言

📦代码仓库链接 react-todo gitee仓库

💻在线预览效果 react-todo 开发进度

# 👀从零开始学React第一天~React基础框架的构建(Create React App+Tailwind css+Material ui)

# 👀从零开始学React第二天~React配置Eslint+路由导航的实现(react-router-dom)

# 👀从零开始学React第三天~React日期选择器组件开发+Dayjs的使用

# 👀从零开始学React第四天~📆实现一个好看的弹窗日历组件

# 👀从零开始学React第五天~📈实现hook相关性能优化

# 组件再多也不怕!📦Redux+Redux Toolkit概念介绍与实战使用

状态持久化简介

首先要理解状态持久化是什么意思,简单来说就是将网页中的数据给存起来,避免咱们页面一关或者一按刷新数据就没了。一般来说咱们的大部分数据都是存储在 服务器数据库 中,通过网络请求来获取,那么前端也就不需要担心数据丢失的问题。

但是一些简单的数据存储在客户端会更加方便,例如网站 页面的导航的当前选中项弹窗的显示与隐藏页面滚动的位置。这些都可以直接存在客户端中。持久化操作不仅会降低用户刷新页面时样式变化导致的割裂感,还可以减少请求的次数和每次传输的数据量以提升性能。

在浏览器中我们通常使用 localStoragesessionStroage 相关的api进行一个客户端存储,它们的具体使用这里不赘述。我们在使用一些前端框架时,通常会同时使用一些相关的状态管理工具,例如 vuexredux,通过这些工具可以帮助我们快速实现全局状态的管理,减少组件间 大量业务不相关 数据的流动,并且提供全局数据的一些响应式操作。

但是这些状态管理工具通常都是将数据存储在临时内存中,我们页面一刷新就重新初始化数据了。因此,我们可以将 状态管理工具 创建的仓库进行一个持久化操作,这样既能所有组件快速共享数据,还能保证数据不会因为网站一关就销毁了。

这篇文章主要介绍的是 react 中使用 redux-toolkit 实现状态管理的持久化改造,使用的库是#redux-presist。前面使用 redux-toolkit 构建全局状态管理可以参考这一篇文章~ # 组件再多也不怕!📦Redux+Redux Toolkit概念介绍与实战使用

实现持久化

安装

安装redux-presist

yarn add redux-persist

配置

redux-presist 的文档中,为我们提供了一个引入的示例代码:

import { createStore } from 'redux'
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage' // defaults to localStorage for web

import rootReducer from './reducers'

const persistConfig = {
  key: 'root',
  storage,
}

const persistedReducer = persistReducer(persistConfig, rootReducer)

export default () => {
  let store = createStore(persistedReducer)
  let persistor = persistStore(store)
  return { store, persistor }
}

但是以上的示例代码使用的是 reduxcreateStore api进行创建仓库的,而我们在前面的初始化中使用的是 redux-toolkit 提供的 configureStore 进行初始化的,如果直接对照官方提供的案例可能会一时摸不着头脑要怎么去改。

下面我先放下我引入 redux-presist 前redux入口文件的代码:

// store/index.js
import { configureStore } from "@reduxjs/toolkit"
import date from "./modules/date"
import todo from "./modules/todo"
import drawer from "./modules/drawer"

export default configureStore({
    reducer: {
        date,
        todo,
        drawer,
        // 这里放入各个模块

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

根据上面的代码以及redux-presist的实例代码分析,我们缺少的有两个变量,一个是rootReducer,还有一个是 store ,那么这两个变量从哪里来呢?

生成rootReducer

首先是 rootReducer ,我们在configureStore时传入的参数中有一个对象reducer,我传入了我定义的各个模块分片,所以我就在想会不会就是这个对象呢?其实不是的,如果我们要将各个 Slice Reducer切片 整合成一个大的切片,我们需要使用到 combineReducers 这个api。

combineReducers 辅助函数的作用是,把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore 方法。

在合并后通过为传入对象的 reducer 命名不同的 key 来控制返回 state key 的命名。例如,你可以调用 combineReducers({ todos: myTodosReducer, counter: myCounterReducer }) 将 state 结构变为 { todos, counter }

所以我们要修改一下代码,将我们的各个模块用 combineReducers 合并起来,然后将 persistConfig合并的模块 作为persistReducer的参数传入。

import { persistStore, persistReducer } from "redux-persist"
import { combineReducers } from "redux"
import storage from "redux-persist/lib/storage"

const persistConfig = {
  key: 'root',
  storage,
}
const persistedReducer = persistReducer(
  persistConfig,
  combineReducers({
    date,
    todo,
    drawer,
    // 这里放入各个模块
  })
)

redux-presist存储引擎

这里讲一下 persistConfig 这个api的参数 storage 的作用,前面有说到在浏览器中我们可以使用localStoragesessionStroage 进行客户端存储。在redux-persist中内置了很多存储引擎,因为react项目不仅仅只能跑在浏览器中,还可以编译成小程序,app等等其他平台,因此要根据自己所开发的平台进行存储引擎的选择。而 storage 对应的就是 localStorage,下图是官方提供的所有存储引擎。

如何在React中实现全局数据的状态持久化?一篇文章让你看懂状态持久化

生成store

rootReducer 解决了,那么接下来我们就看看store从哪来,其实非常简单,我们原本代码中configureStore 这个api就是用于创建仓库的,那么它的返回值就是一个 store,最终入口文件的改造代码如下:

// store/index.js
import { configureStore } from "@reduxjs/toolkit"
import { persistStore, persistReducer } from "redux-persist"
import { combineReducers } from "redux"
import storage from "redux-persist/lib/storage"
import date from "./modules/date"
import todo from "./modules/todo"
import drawer from "./modules/drawer"

const persistConfig = {
  key: "root",
  storage,
}

const persistedReducer = persistReducer(
  persistConfig,
  combineReducers({
    date,
    todo,
    drawer,
    // 这里放入各个模块
  })
)

export const store = configureStore({
  reducer: persistedReducer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: false,
    }),
})
export const persistor = persistStore(store)

接下来我们再修改一下 index.js,这里按照官方的示例来操作就OK,我实现的代码如下:

import React from "react"
import ReactDOM from "react-dom"
import "./index.css"
import App from "./App"
import reportWebVitals from "./reportWebVitals"
import { BrowserRouter } from "react-router-dom"
import { store, persistor } from "./store/index"
import { PersistGate } from "redux-persist/integration/react"
import { Provider } from "react-redux"
ReactDOM.render(
  <Provider store={store}>
    <PersistGate loading={null} persistor={persistor}>
      <BrowserRouter>
        <React.StrictMode>
          <App />
        </React.StrictMode>
      </BrowserRouter>
    </PersistGate>
  </Provider>,
  document.getElementById("root")
)

检验

在配置完成后,我们可以打开浏览器的开发者工具,在顶部 Application 菜单中,选择左侧 Local Storage ,我们就可以看到多了一条数据 persist:root ,这里存储的就是我们的全局状态,当然具体你也配置只存放部分的模块,具体根据官方文档所说来操作即可。

如何在React中实现全局数据的状态持久化?一篇文章让你看懂状态持久化

总结

这篇文章首先介绍了状态持久化的意思和作用,然后实现了一下 redux-tookit创建仓库的持久化改造,最后成功的在 localstorage 中看到了我们存储的数据。

如果觉得文章写不错或者对你有帮助可以 点个赞 表扬一下~ vuex的文章也在路上了,欢迎关注插眼,尽请期待!