填坑之路

填坑之路:React状态管理

2020-11-06  本文已影响0人  哦啦吧啦丶

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

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

前言

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

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

父子相连

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

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

组件树

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


逻辑混乱

怎么办???

方案

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

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

我们会想:能不能有一种实践规范,将所有可能公用的状态、数据及能力提取到组件外,数据流自上往下,哪里需要哪里自己获取,而不是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一大特点。

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

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需要监听视图中数据的变动来保证视图实时更新,即

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

(手动分割线)

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

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

上一篇 下一篇

猜你喜欢

热点阅读