如何在React中实现全局数据的状态持久化?一篇文章让你看懂状态持久化
前言
📦代码仓库链接 react-todo gitee仓库
💻在线预览效果 react-todo 开发进度
# 👀从零开始学React第一天~React基础框架的构建(Create React App+Tailwind css+Material ui)
# 👀从零开始学React第二天~React配置Eslint+路由导航的实现(react-router-dom)
# 👀从零开始学React第三天~React日期选择器组件开发+Dayjs的使用
# 👀从零开始学React第四天~📆实现一个好看的弹窗日历组件
状态持久化简介
首先要理解状态持久化是什么意思,简单来说就是将网页中的数据给存起来,避免咱们页面一关或者一按刷新数据就没了。一般来说咱们的大部分数据都是存储在 服务器数据库 中,通过网络请求来获取,那么前端也就不需要担心数据丢失的问题。
但是一些简单的数据存储在客户端会更加方便,例如网站 页面的导航的当前选中项,弹窗的显示与隐藏,页面滚动的位置。这些都可以直接存在客户端中。持久化操作不仅会降低用户刷新页面时样式变化导致的割裂感,还可以减少请求的次数和每次传输的数据量以提升性能。
在浏览器中我们通常使用 localStorage 和 sessionStroage 相关的api进行一个客户端存储,它们的具体使用这里不赘述。我们在使用一些前端框架时,通常会同时使用一些相关的状态管理工具,例如 vuex 或 redux,通过这些工具可以帮助我们快速实现全局状态的管理,减少组件间 大量业务不相关 数据的流动,并且提供全局数据的一些响应式操作。
但是这些状态管理工具通常都是将数据存储在临时内存中,我们页面一刷新就重新初始化数据了。因此,我们可以将 状态管理工具 创建的仓库进行一个持久化操作,这样既能所有组件快速共享数据,还能保证数据不会因为网站一关就销毁了。
这篇文章主要介绍的是 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 }
}
但是以上的示例代码使用的是 redux 的 createStore
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 的作用,前面有说到在浏览器中我们可以使用localStorage 和 sessionStroage 进行客户端存储。在redux-persist中内置了很多存储引擎,因为react项目不仅仅只能跑在浏览器中,还可以编译成小程序,app等等其他平台,因此要根据自己所开发的平台进行存储引擎的选择。而 storage 对应的就是 localStorage,下图是官方提供的所有存储引擎。
生成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
,这里存储的就是我们的全局状态,当然具体你也配置只存放部分的模块,具体根据官方文档所说来操作即可。
总结
这篇文章首先介绍了状态持久化的意思和作用,然后实现了一下 redux-tookit创建仓库的持久化改造,最后成功的在 localstorage 中看到了我们存储的数据。
如果觉得文章写不错或者对你有帮助可以 点个赞 表扬一下~ vuex的文章也在路上了,欢迎关注插眼,尽请期待!