使⽤react的hooks进⾏全局的状态管理
使⽤ react 的 hooks 进⾏全局的状态管理
React 最新正式版已经⽀持了 Hooks API,先快速过⼀下新的 API 和⼤概的⽤法。
// useState,简单粗暴,setState可以直接修改整个state
const [state,setState] = useState(value);
// useEffect,⽀持⽣命周期
useEffect(()=>{
// sub
return ()=>{
// unsub
}
},[]);
// useContext,和 ateConext() 配合使⽤。
// ⽗组件使⽤ Context.Provider ⽣产数据,⼦组件使⽤ useContext() 获取数据。
const state = useContext(myContext);
// useReducer,具体⽤法和redux类似,使⽤dispatch(action)修改数据。
// reducer中处理数据并返回新的state
const [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,详情⾃⾏查阅⽂档
⼀开始以为使⽤ useState 分离数据层和 UI 层,就可以达到数据共享了,后来发现想的太简单了。分离只是逻辑共享,数据都是独⽴的。后来发现有个useReducer似乎和 redux 很像,然⽽本质上,还是 useState 实现的。
再继续探索发现,基于useContext,同时配合useReducer⼀起使⽤。
但是,这个⽅案的缺陷是,当数据太⼤,组件太多,会直接导致渲染性能下降。
每⼀次state的变化,都会从顶层组件传递下去,性能影响⽐较⼤。
当然也有⼀些优化⼿段,⽐如使⽤memo()或者useMemo(),⼜或者拆分更细粒度的context,对应不同的数据模块,再包装成不同的ContextProvider,只是这样略显繁琐了。
后⾯,终于到⼀位⼤神的作品,经过简单的修改后得出⼀个可以使⽤的办法:
import { useState,useEffect } from 'react';
const isFunction = fn => typeof fn === 'function';
const isObject = o => typeof o === 'object';
const isPromise = fn => {
if (fn instanceof Promise) return true;
return isObject(fn) && isFunction(fn.then);
};
// Model 类
class Model {
constructor({initialState,actions}){
this.state = initialState;
this.actions = {};
this.queue = [];
Object.keys(actions).forEach((name)=>{
this.actions[name] = (arg)=>{
const res = actions[name].call(this,this.state,arg);
if(isPromise(res)){
this.state = ret;
});
}else{
this.state = res;
}
}
});
}
useStore(){
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);
queues.forEach((setState)=>{
setState(this.state); // 通知所有的组件数据变化
});
}
}
/
/ models/user.js
const sleep = async t => new Promise(resolve => setTimeout(resolve, t));
const initialState = {};
const actions = {
async setUserInfo(){
await sleep(2000);
solve({name:"mannymu",age:18});
},
setAge(state,age){
return Object.assign({},{...state,age});
},
setName(state,name){最新的react面试题
return Object.assign({},{...state,name});
},
loginOut(){
return null;
}
}
const user = new Model({
initialState,
actions
});
/
/ 组件
import {useStore} from '../models/user';
const Person = ()=>{
const [state,actions] = useStore();
return (
<div>
<span> My name is {state.name}.</span>
<button onClick={()=> actions.setName('han meimei.')}>btn1</button>
</div>
)
};
这样就可以使⽤简单的代码,管理复杂的状态了。同时⽀持异步。
当然这⾥是直接采⽤的返回值覆盖 state ,如果异常情况(ajax或者其他报错)处理的不够好,可能会出现奇怪的问题,报错后 return 的值如果
不确定,就会出现奇怪效果。
可以参考 redux 的办法,再扩展⼀个reducer 来专门修改数据,action 只负责触发修改。
通过查看那位⼤佬在 github 上⾯的完整版状态管理插件源码。其基于上⾯的代码基本原理。做了 Proxy 的封装,就可以像 vue ⼀样,直接修改 来更新视图层。
最后,附上我的本地⽂件夹分类:
其中 store.js ⾥⾯,是上⾯的 Model 类。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论