React:Redux进阶
问题引入
描述
上次使用Redux,最终实现了兄弟组件之间的通信--点击左边的导航栏(左边蓝色部分),右边的头部(红色部分)会改变成对应的文字。
问题1.PNG之后又用Redux,准备实现该界面(订单管理)的另一个功能--选中表格中的一栏,点击上面的详细页面按钮(第二个蓝色按钮),可以打开一个新页面,并在该页面上显示选中表格栏的信息。这是一个跨页面的状态通信。点击详细页面按钮的事件代码如下:
openInfoPage = () => {
//如果未选中一条表格栏,弹窗警告
if (!this.state.selectedItem) {
Modal.info({
title: '信息',
content: '请先选择一条订单'
})
return;
}
//将选中的表格栏信息传入Redux的store状态中
this.props.dispatch(orderSelect(this.state.selectedItem))
//设置路由,跳转新页面--(默认打开新窗口;传入参数'_self'则会在本页面打开新页面,此过程只是单存的路由跳转)
window.open(`/#/order/detail/${this.state.selectedItem.id}`,'_self')
}
在实际操作中,若给新打开的页面传入一个'_self'参数,新页面是在目前的页面打开,很幸运,他能完成既定的目标。
问题2.PNG但若是传入参数'_blank'或者不传参,则会单独打开一个新页面。很不幸的是,新页面显示的是store状态的初始值。
问题4.PNG
分析
造成这个现象的原因是:
-
_self 只是单纯的路由跳转,更换一个新的组件(该组件是整个页面)显示在目前页面上,此过程并没有刷新页面。
-
_blank(默认值)是重新打开一个新的页面,每单独打开一个新页面都是涉及到刷新操作的。
因此可以得出,刷新页面会导致store中存储的state(状态值)全部变回初始值。
为了佐证这个想法,我刷新了订单管理界面,发现头部的文字变回“首页”(首页是store中该状态的初始值)
问题3.PNG要解决这个问题,需要学习浏览器的缓存机制。而且Redux中提供了redux-persist来实现状态的持久化
redux-persist
H5本地存储
cookies、sessionStorage和localStorage解释及区别
参考资料,弄懂cookie和webstorage。简单来说就是:
-
cookie在浏览器请求中每次都会附加请求头中发送给服务器。用户代理(一般值浏览器)所实现的大小最少要到达4096字节
-
session:Session用于保存每个用户的专用信息,变量的值保存在服务器端,通过SessionID来区分不同的客户。
-
localStorage保存数据会一直保存没有过期时间,不会随浏览器发送给服务器。大小5M或更大
-
sessionStorage仅当前页面有效一旦关闭就会被释放。也不会随浏览器发送给服务器。大小5M或更大。
redux-persist初体验
我的需求是刷新页面不会导致store里存储的状态值全部丢失被初始化,这些状态在这此网站请求(一次session)过程中是持久化存储的。当明确这些需求后,redux-persist真的是一个很简单的东西,他就是一个在浏览器本地持久化存储状态的方案。
在store.js
中的代码如下,
import menuReducer from './../reducer/menuReducer'
import orderReducer from './../reducer/orderReducer'
import { createStore,combineReducers } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import {persistStore, persistReducer} from 'redux-persist';
import hardSet from 'redux-persist/lib/stateReconciler/hardSet';
import storageSession from 'redux-persist/lib/storage/session'
const reducer = combineReducers({
menu: menuReducer,
order: orderReducer
});
const storageConfig = {
key: 'root', // 必须有的
storage: storageSession, // 缓存机制,默认为localStorage
stateReconciler: hardSet, // 查看 'Merge Process' 部分的具体情况
// whitelist: ['menu','order'] // reducer 里持久化的数据,除此外均为不持久化数据
}
const myPersistReducer = persistReducer(storageConfig, reducer)
const store = createStore(myPersistReducer,composeWithDevTools())
export const persistor = persistStore(store)
export default store
storageConfig
storage的参数选择有storageSession和localStorage
- 因为我的需求只是刷新不会导致状态丢失。storageSession将状态值存入内存,在关闭所有相关的网页后,store存储的状态值才会丢失、初始化;
- 如果要保持状态一直存在,保存在硬盘上,下次登录网页的时候,状态值依然是上次的状态值。可以选择默认的localStorage(store不传参即可)。
stateReconciler的参数有三个选择,根据我的需求选择hardSet即可
- hardSet (import hardSet from 'redux-persist/lib/stateReconciler/hardSet')直接用将来状态替代初始状态
- incoming state: { foo: incomingFoo }
- initial state: { foo: initialFoo, bar: initialBar }
- reconciled state: { foo: incomingFoo } // note bar has been dropped
- autoMergeLevel1 (default) 将来状态和初始状态进行合并,将来状态中的所有成员替换在初始状态中的对应成员,其余初始状态的成员不变
- incoming state: { foo: incomingFoo }
- initial state: { foo: initialFoo, bar: initialBar }
- reconciled state: { foo: incomingFoo, bar: initialBar } // note incomingFoo overwrites initialFoo
- autoMergeLevel2 (import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2') 和autoMergeLevel1类似,但是是浅合并(目前还未没弄清楚浅合并的意义)
- incoming state: { foo: incomingFoo }
- initial state: { foo: initialFoo, bar: initialBar }
- reconciled state: { foo: mergedFoo, bar: initialBar } // note: initialFoo and incomingFoo are shallow merged
最后需要在最顶层的组件中重新设置一下Redux,利用PersistGate包裹根组件。
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import * as serviceWorker from './serviceWorker';
import ERouter from './router'
import { Provider } from 'react-redux'
import store from './redux/store/index'
import {persistor} from './redux/store/index'
import {PersistGate} from 'redux-persist/lib/integration/react';
ReactDOM.render(
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<ERouter />
</PersistGate>
</Provider>,
document.getElementById('root')
)
最后运行效果如下图所示,圆满完成既定目标
结果.PNG总结
匆忙之中,并未完全掌握,后续有用到的话,再进一步的深入学习。