zoukankan      html  css  js  c++  java
  • 使用 react 的 hooks 进行全局的状态管理

    使用 react 的 hooks 进行全局的状态管理

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

    // useState,简单粗暴,setState可以直接修改整个state
    const [state,setState] = useState(value);
     
    // useEffect,支持生命周期
    useEffect(()=>{
        // sub
        return ()=>{
            // unsub
        }
    },[]);
     
    // useContext,和 React.createConext() 配合使用。
    // 父组件使用 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)){
                    Promise.resolve(res).then((ret)=>{
                      this.state = ret;
                      this.onDataChange();
                    });
                  }else{
                    this.state = res;
                    this.onDataChange();
                  }
              }
          });
      }
      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);
            return Promise.resolve({name:"mannymu",age:18});
        },
        setAge(state,age){
            return Object.assign({},{...state,age});
        },
        setName(state,name){
            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 只负责触发修改。

    参考文档:https://blog.csdn.net/tzllxya/article/details/92798097

    参考github: https://github.com/yisbug/iostore  有兴趣的可以看看,这份代码,质量很高,值得一读

    通过查看那位大佬在 github 上面的完整版状态管理插件源码。其基于上面的代码基本原理。做了 Proxy 的封装,就可以像  vue 一样,直接修改 this.xxx 来更新视图层。

    最后,附上我的本地文件夹分类:

    其中 store.js 里面,是上面的 Model 类。

  • 相关阅读:
    详细解释:nginx中ChsHttpProxyModule模块各项配置及其含义
    详细解释:nginx中ngx_http_headers_module模块配置及各个参数含义
    java生成UUID通用唯一识别码 (Universally Unique Identifier)
    详细解释:nginx中gzip的各项配置以及配置参数的意思详解
    详细解释:nginx中ChsHttpUpstreamModule模块配置及各个参数含义
    linux下jdk、tomcat的安装及项目的部署和去掉项目名访问详细总结
    ORACLE中的序列和触发器
    详解之:linux下tomcat、nginx的负载均衡及memcached对session共享的实现配置详细总结
    ASp.net 剖析三层架构
    asp.net简单的注册或者登陆三层架构的例子
  • 原文地址:https://www.cnblogs.com/muamaker/p/11642716.html
Copyright © 2011-2022 走看看