美文网首页填坑之路
填坑之路:React状态管理

填坑之路:React状态管理

作者: 哦啦吧啦丶 | 来源:发表于2020-11-06 17:20 被阅读0次

    文中涉及的React demo代码使用了16.8的新增特性Hooks

    它可以让你在不编写class的情况下使用state以及其他的React特性。

    前言

    刚立项时,可能只有一个根组件Root:管你啥业务,一把梭。

    项目慢慢有起色,好事者就拆出了一些子组件,必然,它们间将有一些数据流动,问题不大,可以让他们“父子相连”。

    父子相连

    现在项目爆了,业务N倍增长,不得不拆出更多的子孙组件出来,实现更多复杂业务。

    但愿逻辑比较简单,数据流动是一层层往下:

    组件树

    现实总是残酷的,往往结构都是这样的,父子孙组件间关系混乱:


    逻辑混乱

    怎么办???

    方案

    只要思想不滑坡,办法总比困难多:

    • 方案1,梳理项目逻辑,重新设计组件(手动Fxxk!!!)
    • 方案2,辞职,换个公司重开(手动狗头)

    项目迭代过程中,不可避免出现组件间状态共享,而导致逻辑交错,难以控制。

    我们会想:能不能有一种实践规范,将所有可能公用的状态、数据及能力提取到组件外,数据流自上往下,哪里需要哪里自己获取,而不是prop drilling,大概如下:

    单向数据流

    于是这样一种数据结构冒了出来:

    const store = {
        state: {
            text: 'Goodbye World!'
        },
        setAction (text) {
            this.text = text
        },
        clearAction () {
            this.text = ''
        }
    }
    
    

    外部变量store,其中state来存储数据,store里面有一堆功能各异的action来控制state的改变。

    我们规定:只能通过调用action来改变state,于是我们就可以通过action清晰地掌握着state的动向,日志、监控、回滚等能力随着而来。

    于是我们大概的看到了Flux的雏形。

    Flux

    2013年,Facebook亮出React的时候,也跟着带出的Flux。Facebook认为两者相辅相成,结合在一起才能构建大型的JavaScript应用。

    做一个容易理解的对比,React是用来替换jQuery的,那么Flux就是以替换Backbone.jsEmber.jsMVC一族框架为目的。

    Flux data flow
    如上图,数据总是“单向流动”,相邻部分不存在互相流动数据的现象,这也是Flux一大特点。
    • View发起用户的Action
    • Dispatcher作为调度中心,接收Action,要求Store进行相应更新
    • Store处理主要逻辑,并提供监听能力,当数据更新后触发监听事件
    • View监听到Store的更新事件后触发UI更新

    感兴趣可以看看每个模块的具体含义:

    Action

    一个普通的Javascript对象,一般使用typepayload描述了该action的具体含义。

    Flux中一般定义actions:一组包含派发action对象的函数。

    // actions.js
    import AddDispatcher from '@/dispatcher'
    
    export const counterActions = {
        increment (number) {
            const action = {
                type: 'INCREMENT',
                payload: number
            }
    
            AddDispatcher.dispatch(action)
        }
    }
    

    以上代码,使用counterActions.increment,将INCREMENT派发到Store

    Dispatcher

    将Action派发到Store,通过flux提供的Dispatcher注册唯一实例。

    Dispatcher.register方法用来登记各种Action的回调函数

    import { CounterStore } from '@/store'
    import AddDispatcher from '@/dispatcher'
    
    AppDispatcher.register(function (action) {
      switch (action.type) {
        case INCREMENT:
          CounterStore.addHandler();
          CounterStore.emitChange();
          break;
        default:
        // no op
      }
    });
    

    以上代码,AppDispatcher收到INCREMENT动作,就会执行回调函数,对CounterStore进行操作。

    Dispatcher只用来派发Action,不应该有其他逻辑。

    Store

    应用状态的处理中心。

    Store中复杂处理业务逻辑,而由于数据变更后View需要更新,所以它也负责提供通知视图更新的能力。

    因为其随用随注册,一个应用可以注册多个Store的能力,更新data flow为:

    mul-store

    细心的朋友可以发现在上一小节CounterStore中调用了emitChange的方法,对,它就是用来通知变更的。

    import { EventEmitter } from "events"
    
    export const CounterStore = Object.assign({}, EventEmitter.prototype, {
      counter: 0,
      getCounter: function () {
        return this.counter
      },
      addHandler: function () {
        this.counter++
      },
      emitChange: function () {
        this.emit("change")
      },
      addChangeListener: function (callback) {
        this.on("change", callback)
      },
      removeChangeListener: function (callback) {
        this.removeListener("change", callback)
      }
    });
    

    以上代码,CounterStore通过继承EventEmitter.prototype获得触发emit与监听on事件能力。

    View

    Store中的数据的视图展示

    View需要监听视图中数据的变动来保证视图实时更新,即

    • 在组件中需要添加addChangeListerner
    • 在组件销毁时移除监听removeChangeListener

    我们看个简单的Couter例子,感受下Flux的代码。

    (手动分割线)

    认真体验的朋友可能会注意到:

    • 点击reset后,store中的couter被更新(没有emitChange 所以没实时更新视图);
    • 业务逻辑与数据处理逻辑交错,代码组织混乱;

    好,打住,再看个新的数据流。

    相关文章

      网友评论

        本文标题:填坑之路:React状态管理

        本文链接:https://www.haomeiwen.com/subject/xratbktx.html