redux持久化之redux-persist結合immutable使用問題
前言
最近學習瞭redux以及react-redux的結合使用確實讓redux在react中更好的輸出代碼啦~
但是考慮到項目的各種需求,我們還是需要對redux進行深一步的改造,讓其能更好的滿足我們的日常開發,大大提高我們的開發效率。
今天給大傢推薦兩個好用的功能包,並解決一個它們結合使用存在的問題。
redux-persist
redux-persist 主要用於幫助我們實現redux的狀態持久化
所謂狀態持久化就是將狀態與本地存儲聯系起來,達到刷新或者關閉重新打開後依然能得到保存的狀態。
安裝
yarn add redux-persist // 或者 npm i redux-persist
Github 地址 https://github.com/rt2zz/redux-persist
大傢可以去看看官方的說明文檔,這裡就不一一介紹功能瞭,簡單講一點常用功能和導入到項目使用。
使用到項目上
store.js
帶有 // ** 標識註釋的就是需要安裝後添加進去使用的一些配置,大傢好好對比下投擲哦
下面文件也是一樣
import { createStore, applyMiddleware, compose } from "redux"; import thunk from 'redux-thunk' import { persistStore, persistReducer } from 'redux-persist' // ** import storage from 'redux-persist/lib/storage' // ** import reducer from './reducer' const persistConfig = { // ** key: 'root',// 儲存的標識名 storage, // 儲存方式 whitelist: ['persistReducer'] //白名單 模塊參與緩存 } const persistedReducer = persistReducer(persistConfig, reducer) // ** const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; const store = createStore(persistedReducer, composeEnhancers(applyMiddleware(thunk))) // ** const persistor = persistStore(store) // ** export { // ** store, persistor }
index.js
import React from 'react'; import ReactDOM from 'react-dom/client'; import { Provider } from 'react-redux' import { BrowserRouter } from 'react-router-dom' import { PersistGate } from 'redux-persist/integration/react' // ** import { store, persistor } from './store' // ** import 'antd/dist/antd.min.css'; import './index.css'; import App from './App'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <Provider store={store}> {/* 使用PersistGate //** */} <PersistGate loading={null} persistor={persistor}> <BrowserRouter> <App /> </BrowserRouter> </PersistGate> </Provider> </React.StrictMode> );
persist_reducer.js
註意此時的模塊是在白名單之內,這樣persist_reducer的狀態就會進行持久化處理瞭
import { DECREMENT } from './constant' const defaultState = ({ count: 1000, title: 'redux 持久化測試' }) const reducer = (preState = defaultState, actions) => { const { type, count } = actions switch (type) { case DECREMENT: return { ...preState, count: preState.count - count * 1 } default: return preState } } export default reducer
這樣就可以使用起來瞭,更多的配置可以看看上面Github的地址上的說明文檔
immutable
immutable 主要配合我們redux的狀態來使用,因為reducer必須保證是一個純函數,所以我們當狀態中有引用類型的值時我們可能進行淺拷貝來處理,或者遇到深層次的引用類型嵌套時我們采用深拷貝來處理。
但是我們會覺得這樣的處理確實稍微麻煩,而且我們若是采用簡單的深拷貝 JSON.parse JSON.stringify 來處理也是不靠譜的,存在缺陷 就比如屬性值為undefined 時會忽略該屬性。
所以 immutable 就是來幫我們解決這些問題,使用它修改後會到的一個新的引用地址,且它並不是完全復制的,它會盡可能的利用到未修改的引用地址來進行復用,比起傳統的深拷貝性能確實好很多。
這裡就不多說瞭,想要瞭解更多可以看看下面的GitHub官網說明文檔。
安裝
npm install immutable // 或者 yarn add immutable
GitHub地址 https://github.com/immutable-js/immutable-js
使用到項目上
count_reducer.js
import { INCREMENT } from './constant' import { Map } from 'immutable' // 簡單的結構用Map就行 復雜使用fromJs 讀取和設置都可以getIn setIn ... const defaultState = Map({ // ** count: 0, title: '計算求和案例' }) const reducer = (preState = defaultState, actions) => { const { type, count } = actions switch (type) { case INCREMENT: // return { ...preState, count: preState.count + count * 1 } return preState.set('count', preState.get('count') + count * 1) // ** default: return preState } } export default reducer
讀取和派發如下 : 派發無需變化,就是取值時需要get
函數組件
const dispatch = useDispatch() const { count, title } = useSelector(state => ({ count: state.countReducer.get("count"), title: state.countReducer.get("title") }), shallowEqual) const handleAdd = () => { const { value } = inputRef.current dispatch(incrementAction(value)) } const handleAddAsync = () => { const { value } = inputRef.current dispatch(incrementAsyncAction(value, 2000)) }
類組件
class RedexTest extends Component { // ....略 render() { const { count, title } = this.props return ( <div> <h2>Redux-test:{title}</h2> <h3>count:{count}</h3> <input type="text" ref={r => this.inputRef = r} /> <button onClick={this.handleAdd}>+++</button> <button onClick={this.handleAddAsync}>asyncAdd</button> </div> ) } } //使用connect()()創建並暴露一個Count的容器組件 export default connect( state => ({ count: state.countReducer.get('count'), title: state.countReducer.get('title') }), { incrementAdd: incrementAction, incrementAsyncAdd: incrementAsyncAction } )(RedexTest)
這樣就可以使用起來瞭,更多的配置可以看看上面Github的地址上的說明文檔
結合使用存在的問題
結合使用有一個坑!!!
是這樣的,當我們使用瞭redux-persist 它會每次對我們的狀態保存到本地並返回給我們,但是如果使用瞭immutable進行處理,把默認狀態改成一種它內部定制Map結構,此時我們再傳給 redux-persist,它倒是不挑食能解析,但是它返回的結構變瞭,不再是之前那個Map結構瞭而是普通的對象,所以此時我們再在reducer操作它時就報錯瞭
如下案例:
組件
import React, { memo } from "react"; import { useDispatch, useSelector, shallowEqual } from "react-redux"; import { incrementAdd } from "../store/persist_action"; const ReduxPersist = memo(() => { const dispatch = useDispatch(); const { count, title } = useSelector( ({ persistReducer }) => ({ count: persistReducer.get("count"), title: persistReducer.get("title"), }), shallowEqual ); return ( <div> <h2>ReduxPersist----{title}</h2> <h3>count:{count}</h3> <button onClick={(e) => dispatch(incrementAdd(10))}>-10</button> </div> ); }); export default ReduxPersist;
persist-reducer.js
import { DECREMENT } from './constant' import { fromJS } from 'immutable' const defaultState = fromJS({ count: 1000, title: 'redux 持久化測試' }) const reducer = (preState = defaultState, actions) => { const { type, count } = actions switch (type) { case DECREMENT: return preState.set('count', preState.get('count') - count * 1) default: return preState } } export default reducer
按理說是正常顯示,但是呢由於該reducer是被redux-persist處理的,所以呢就報錯瞭
報錯提示我們沒有這個 get 方法瞭,即表示變成瞭普通對象
解決
組件
import React, { memo } from "react"; import { useDispatch, useSelector, shallowEqual } from "react-redux"; import { incrementAdd } from "../store/persist_action"; const ReduxPersist = memo(() => { const dispatch = useDispatch(); // ** const { count, title } = useSelector( ({ persistReducer: { count, title } }) => ({ count, title, }), shallowEqual ); //const { count, title } = useSelector( // ({ persistReducer }) => ({ // count: persistReducer.get("count"), // title: persistReducer.get("title"), // }), // shallowEqual // ); return ( <div> <h2>ReduxPersist----{title}</h2> <h3>count:{count}</h3> <button onClick={(e) => dispatch(incrementAdd(10))}>-10</button> </div> ); }); export default ReduxPersist;
persist-reducer.js
import { DECREMENT } from './constant' import { fromJS } from 'immutable' const defaultState = ({ // ** count: 1000, title: 'redux 持久化測試' }) const reducer = (preState = defaultState, actions) => { const { type, count } = actions let mapObj = fromJS(preState) // ** switch (type) { case DECREMENT: // return preState.set('count', preState.get('count') - count * 1) return mapObj.set('count', mapObj.get('count') - count * 1).toJS() // ** default: return preState } } export default reducer
解決思路
由於 redux-persist 處理每次會返回普通對象,所以我們隻能等要在reducer中處理狀態時,我們先將其用immutable處理成它內部定制Map結構,然後我們再進行set操作修改,最後我們又將Map結構轉換為普通對象輸出,這樣就完美的解決瞭這個問題。
以上就是redux持久化之redux-persist結合immutable使用問題的詳細內容,更多關於redux持久化redux-persist的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- React Redux使用配置詳解
- 一文搞懂redux在react中的初步用法
- React 中使用 Redux 的 4 種寫法小結
- 使用react+redux實現計數器功能及遇到問題
- 詳解React 和 Redux的關系