react全局状态管理_如何利用 React Hooks API 做全局状态管理?

React 最新正式版已经支持了 Hooks API,先快速过一下新的 API 和大概的用法。

// useState,简单粗暴,setState可以直接修改整个stateconst [state,setState] = useState(value);

// useEffect,支持生命周期useEffect(()=>{

// sub return ()=>{

// unsub }

},[]);

// useContext,和 React.createConext() 配合使用。// 父组件使用 Context.Provider 生产数据,子组件使用 useContext() 获取数据。const state = useContext(myContext);

// useReducer,具体用法和redux类似,使用dispatch(action)修改数据。// reducer中处理数据并返回新的stateconst [state, dispatch] = useReducer(reducer, initialState);

// useCallback,返回一个memoized函数,第二个参数类似useEffect,只有参数变化时才会更改。const memoizedCallback = useCallback(

() => {

doSomething(a, b);

},

[a, b],

);

// useMemo,返回一个memoized值,只有第二个参数发生变化时才会重新计算。类似 useCallback。// useCallback(fn,inputs) 等效 useMemo(() => fn,inputs)。const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b])

// useRef,返回一个可变的ref对象const refContainer = useRef(initialValue);

// useImperativeMethods,详情自行查阅文档// useMutationEffect,类似useEffect,详情自行查阅文档// useLayoutEffect,类似useEffect,详情自行查阅文档

那么在新的 API 支持下,如何做全局的状态管理呢?

首先我们看到官方提供了 useReducer 方法,可以实现类似redux的效果。

但是这个方案有一个明显的问题,这里定义的state是和组件绑定的,和useState一样,无法和其他组件共享数据。其实useReducer内部也是用useState实现的。

另外一个方案,基于useContext,同时配合useReducer一起使用。

我们知道,React.createContext()是一种生产消费者的模式,我们可以在组件树顶层使用Context.Provider生产/改变数据,在子组件使用Context.Consumer消费数据。

那么基于这一点,我们可以在顶层组件使用const [state,dispatch] = useReducer(reducer, initialState),然后将返回的state以及dispatch方法传递给Context.Provider,变通的实现数据共享,大约类似下面的代码:

const myContext = React.createContext();

const ContextProvider = ()=>{

const [state, dispatch] = useReducer(reducer, { count: 0 });

return (

{props.children}

);

};

然后就可以在子组件使用const { state, dispatch } = useContext(myContext);获取到全局的state和dispatch了。

但是,这个方案的缺陷是,当数据太大,组件太多,会直接导致渲染性能下降。

每一次state的变化,都会从顶层组件传递下去,性能影响比较大。

当然也有一些优化手段,比如使用memo()或者useMemo(),又或者拆分更细粒度的context,对应不同的数据模块,再包装成不同的ContextProvider,只是这样略显繁琐了。

那么还有木有其他的方案呢?

我们知道在使用hooks时,顺序非常重要,并且不允许在条件分支、循环嵌套等代码中使用。

稍微了解一下 hooks 的实现原理,每一个无状态组件在调用 Hooks API 时,会将 state 存储在当前组件对象的一个叫做memoizedState的属性中,类似这样:

// 假设一个屋状态组件被解析成这样一个对象:const obj = {

..., // 其他的属性 memoizedState:{

memoizedState, // 存储第一次调用 useState 的 state next: {

memoizedState, // 存储第二次调用 useState 的 state next:{

memoizedState, // 第三次 next, // 继续,如果有更多次调用的话。 }

}

}

}

那么我们是不是通过实现一个发布订阅的模式,来将各个不同的组件和中心化的store之间建立一个关联关系?

流程如下:store 内部实现一个useModel方法,内部调用useState,但返回全局的store.state和store.dispatch。这一步实现了全局数据共享。

store 内部将每次调用useState返回的setState方法储存到一个队列。

当store的数据发生变化时,按顺序调用队列里的setState方法,触发每个子组件的渲染。

根据调用hooks是有顺序的这个特点,利用useEffect方法,安全的订阅和取消订阅,避免组件销毁了仍然通知组件数据变化。

一个粗糙的例子:

// Model 类class Model {

state:{

name: 'lilei'

},

actions:{},

queue: [],

constructor({initialState,actions}){

this.state = initialState;

this.actions = {};

Object.keys(actions).forEach((name)=>{

this.actions[name] = (...args)=>{

this.state = actions[name].apply(this,args);

this.onDataChange();

}

});

},

useModel(){

const [, setState] = useState();

// 使用useEffect实现发布订阅 useEffect(() => {

const index = this.queue.length;

this.queue.push(setState); // 订阅 return () => { // 组件销毁时取消 this.queue.splice(index, 1);

};

});

return [this.state, this.actions];

},

onDataChange(){

const queues = [].concat(this.queue);

this.queue.length = 0;

queues.forEach((setState)=>{

setState(this.state); // 通知所有的组件数据变化 });

}

}

// models/user.jsconst user = new Model({

initialState,

actions:{

changeName(name){

return {name};

}

}

});

// 组件import {useModel} from '../models/user';

const Person = ()=>{

const [state,actions] = useModel();

return (

My name is {state.name}.

actions.changeName('han meimei.')}>btn1

)

};

我实现了一个稍微完整一些的例子,代码也不复杂,大约100行的样子,其中支持了异步方法、loading的处理,有兴趣的可以移步github看看,地址在这里:

目前的实现还比较粗糙,也借鉴了很多其他方案。 但相比其他方案来说,如果理解了 Hooks 的用法,上手是非常简单的,没有太多的概念。

并且基于这个思路,也可以很容易的扩展一些功能,实现一个比较完整的model层,再通过一个单例的store统一管理所有model,例如实现这样的效果:

import {useModel} from 'store';

const [userState, user] = useModel('user');

const [articleState, article] = useModel('article');

userEffect(()=>{

user.getUserInfo();

article.getList();

},[]);

这里只是提供了一些思路,除了以上,还有木有其他的方案呢?欢迎留言讨论哈~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值