hooks的出现改变了传统compoent用class 书写的方式,生态对其支持还是属于适应阶段。redux状态管理在纯函数组件上的应用似乎变得棘手。虽然给出一些类似于useContext、useReducer的方式,似乎能将redux应用于这种function方式的组件,但是一探究竟后你就会发现,这些api还是很局限,不能达到redux及react-redux这一套状态管理和视图同步的效果。
下面一起来探索
1、redux、react-redux是完美方案吗?
先给出答案,是。
为什么,实现了状态的管理和共享,局部属性的改变最小化更新ui,达到最优的性能。
2、useReducer
使用useReducer 可以实现redux的状态单向数据流,但局限于纯函数内部,不能实现跨组件状态共享。
3、useContext
useContext能弥补useReducer的不足,做到全局状态的共享和redux的数据流方式。
但是,另一个问题诞生,状态改变不会引起ui的更新。
为什么,如我在根节点下面定义一个useReducer,拿到状态通过属性传给子组件,那么可以实现状态的管理和更新,劣势是每次更新都会是整个组件更新,新能极差。 如果我不绑定给子组件,通过useContext子组件自己获取,那么子组件能正常获取到全局的状态,也可以dispatch状态,但是状态的改变不能跟ui绑定。
4、我们的方案 useContext+redux+自定义hook
useContext和redux用法完全同之前的用法。我们的自定义hook做了哪些工作:
1、通过useContext获取全局状态。
2、根据调用组件的mapstatetoprops给组件设定组件自己的状态。
3、监控store状态的变化,当状态变化时,比较当前组件map的属性变化(浅比较即可),如果组件状态,会通过useState()中的改变状态函数去改变当前组件的状态。实现组件的ui重绘。
4、组件被卸载后,会去掉状态订阅。
//使用
export default function PageHead(props) {
const mapStateToProps = (state) => {
return {
motoAdvert: state.motoAdvert,
}
}
const mapDispatchToProps = (dispatch) => {
return {
motoBannerClick: ({ groupData, index }) =>
dispatch({ type: 'click:headMotoBanner', groupData, index })
}
}
const [{
motoAdvert
}, {
motoBannerClick
}] = useReactRedux(mapStateToProps, mapDispatchToProps)
const headMotoList = get(motoAdvert, 'list', [])
return (
<div >
<Flex className="main-0618-motoadvert">
<ImgList
noHolder
className={`main-0618-motoadvert-banner ${headMotoList.length === 1 ? 'only-one' : ''}`}
groupData={motoAdvert}
limited={2}
onClick={motoBannerClick}
/>
</Flex>
</div >
)
}
import React, { Component, useState, useEffect, useRef, useContext } from 'react'
import { storeContext } from '../js/store'
export default function useReactRedux(mapStateToProps, mapDispatchToProps) {
const store = useContext(storeContext) // 拿到store
const { dispatch, subscribe, getState } = store
// map映射
const currentState = mapStateToProps && mapStateToProps(getState()) || {}
const currentDispatchs = mapDispatchToProps && mapDispatchToProps(dispatch) || {}
const [componentState, setComponentState] = useState(currentState)
const [componentDispatchs, setComponentDispatchs] = useState(currentDispatchs)
// 状态订阅
useEffect(() => {
//缓存 上次状态
let lastComponetState
let mySubscribe = subscribe(() => {
const newCompentState = mapStateToProps(getState())
if (!isObjEqual(lastComponetState, newCompentState)) {
setComponentState(newCompentState)
lastComponetState = newCompentState
}
})
return ()=> mySubscribe() //去掉订阅
}, [])
return [componentState, componentDispatchs]
}
function isObjEqual(o1, o2) {
if (!o1 || !o2) {
return false
}
var props1 = Object.getOwnPropertyNames(o1);
var props2 = Object.getOwnPropertyNames(o2);
if (props1.length != props2.length) {
return false;
}
for (var i = 0, max = props1.length; i < max; i++) {
var propName = props1[i];
if (o1[propName] !== o2[propName]) {
return false;
}
}
return true;
}
我们的优势
1、 写法完全同react-redux 的connect方式。简化了接入成本和老项目翻新成本。
2、 实现了全局状态按需给到组件。实现最小化的ui更新。
3、 实现了纯函数编程的方式,避免高阶函数的嵌套地狱。
4、 甩掉了传统的class方式繁琐的生命周期,让开发变得更快乐。