React为何须要Flux-like的库

从学习React到如今的一点感觉

我以为应该有很多同窗和我同样,上来学React,以为甚是惊艳,看着看着,发现facebook 安利了一个flux,图画的巨复杂,而后各类例子都有用这个东西,没办法,硬着头皮看。看得似懂非懂,而后忽然你们又都推荐Redux,号称最简单的flux-like的实现,结果实现的源码是很简单,可是文档是源码的几十倍,概念甩一脸,写个简单东西,要建十几个文件,写得云里雾里。html

有没有想到,为何要用Flux这类东西?本篇的定位是让你们知道有个脉络,因此不会太注意措辞的准确性.react

React的数据流向

React只是一个view层的解决方案,光有界面没用,还得加上数据才行。React经过propsstate去渲染界面,因此有个很形象的描述UI = f(props,state).android

有数据,就有数据通讯的问题。react是单向数据流,父组件经过props把数据传递给子组件。可是数据的流向不可能只有这一种。git

  1. 祖父组件到孙子组件。
    这个看上去只是父到子的衍生,可是祖祖祖父到孙子组件呢。这个react在(好像是)0.13时经过context基本解决了,关于context,以前没接触的同窗,能够看文档github

  2. 子到父。下面是一种方案:父给子传递一个函数,子在只要调用这个函数,父就能获得相关的数据。可是孙子到祖祖祖父呢。。编程

  3. 非父子关系:基本能够叫作是兄弟关系,以网页为例,总归有一个共同的祖先<body>,可是有多是很是很是远的兄弟。这个怎么处理。redux

组件的一些关系和相应的通讯方式,官方有简单的说明,见文档设计模式

对于上面的23两点,用react本事的机制,写出来都很别扭,特别是第3点。两个不相关的地方,要数据通讯,最简单就是一个全局变量吗。固然光有全局变量还不行,你改了全局变量,其余全部对这个变量感兴趣React组件的都要被通知到,这样才能相应改变界面。你若是接触到设计模式,应该能想到观察者模式(中介者模式也能够,实际上flux更像中介者中,不过观察者应该接受度更高点,并且这里不影响理解)。服务器

其实官方文档中,也有些小线索。angular2

For communication between two components that don't have a parent-child relationship, you can set up your own global event system. Subscribe to events in componentDidMount(), unsubscribe in componentWillUnmount(), and call setState() when you receive an event. Flux pattern is one of the possible ways to arrange this.

这段的关键是you can set up your own global event system。因此你只要去研究之前各类事件系统是怎么设计,就能够本身撸一套 **ux了。

**UX的关键构成

梳理了一下,React须要配合Flux-Like的库去使用,是由于要解决通讯问题。通讯的关键有一下几点:

  1. 数据,这个不用说的吧。由于react中,经过setState去触发改变界面,命名成state

  2. 各类事件,叫event可能更直观,可是你们都叫它action,一个意思,发生了一个动做。这个动做有一下几个关键属性:什么动做,谁发出的(这个我看各个类flux库中,好像都没处理),有没有额外信息吗。

  3. 事件发生,要分发出去,包括改变数据,而后通知给全部监听数据变化的Listeners

  4. 注册监听者。

这些都是你们能想象到。好了,根据这几点,一个简单的Myux就能够写了

class Myux{
    state:{},
    actionTypes:{},
    dispatch(){},
    subscribe(){}
    listeners:[]
}

与上面四个组成部分项对应。来个例子吧,以计数器为例吧。

class CountStore{
    static actionTypes:{
        UP:'UP', //你英语好,你用increase
        DOWN:"DOWN"
    }
    
    state:0 //数据,计数开始是零
    listeners:[]
    
    dispatch(actionType){
        if(actionType === CountStore.actionTypes.UP){
            this.state++;
        }
        if(actionType === CountStore.actionTypes.DOWN){
            this.state--;
        }
        this.listeners.forEach((ln)=>{
            ln(actionType,this,undefined)//对应什么动做,谁发出的,额外信息。
        })
    }
    
    subscribe(ln){
        this.listeners.push(ln)
        //返回一个函数,调用,就取消注册
        return ()=>{
            const index = this.listeners.indexOf(ln);
            if(index !== -1){
                this.listeners.splice(index,1)
            }
        }
    }
}

react的组件里,只要注册成listener,而后state发生变化,被通知到,调用setState进行视图更新就好。

class CountComponent extends React.Component{
    constructor(props,context){
        super(props,context)
        const store = this.props.store;
        this.state = store.getState();
        
    }
    
    componentDidMount(){
        this.unsubscribe = store.subscribe((actionType,store)=>{
            if(this.state !== store.getState()){
                this.setState(store.getState());
            }
        })
    }
    
    componentWillUnmount(){
        if(typeof this.unsubscribe === 'function'){
            this.unsubscribe();
        }
    }
    
    
    render(){
        const state = this.state
        return <div>{state}</div>
    }
}

使用吗,直接mount到body上,会报warning,忽略...

const countStore = new CountStore()
ReactDOM.render(
    <CountComponent store={countStore}/>,
    document.body
)

这样,只要在任何地方,countStore.dispatch(upOrDown),CountComponent里的数字就会加加减减。
能够想一想一下,若是页面有2,3个组件要根据计数器的数值,作界面的相应变化,都是能够轻松知足的。

固然,若是只有一个组件用须要这个store,那么单纯代码上,这样写,要多写不少东西。可是谁知道之后页面不会加一个要共用这个store的组件,这时候这个store就是组件间通讯的法宝了。

实际状况要复杂

上面只有一个store,这是store还只有一个state,这个太简单了。实际,你的应用可能要维护多个状态。怎么办

  1. 一个store里一个state,而后多个store

    ListStore => ListState
        DetailStore => DetailState
  2. 全局就一个store,state是一个状态树,整个应用须要的state,都在这个树里。

    GlobalStore => state:{list:[],detail:{}} //...

    还有一个问题dispatch,是全局一个dispatch,仍是每一个store一个dispatch

这些分歧,加上函数式等,就致使了有flux,reflux,redux。。

还有各个事件之间,有可能存在依赖关系,A事件后,B也触发。又要加waitFor中间件等概念。不过总体来讲,就这些东西。

各类库特色大串烧

redux的特色

redux的文档里,有三大原则,有了上面的概念,咱们来对照看一下

  1. Single source of truth:就是更改应用一个state tree,储存在一个store里,这种状况,也只能有一个dispatch

  2. State is read-only:state是全局的,若是不是只读的,很难维护。这个上面没有体现,可是也是很天然的想法。

  3. Mutations are written as pure functions:这个算redux最大的特色,引入了reducers的概念,和第二点有相辅相成的感受。

另外还有中间件系统。二、3两点,实际上是函数式编程的基本概念,不变量pure function。相比于传统的事件系统,Redux融入了很多functional reactive programming(FRP)的思想。

flux的特色

单dispatch,多store多state,用waitFor处理store的依赖。

reflux

多dispatch,多store多state。这个并没实际用过,看文档,应该是这样的。有问题,请提出。

后记

如今有种趋势,传统的事件系统逐渐让你们以为low b,functional reactive programming(FRP)高大上。
Redux有一些FRP的思想,被你们以为比Flux高大上,可是不是很复杂的项目,应该会有:卧槽,那么简单的东西,为毛有那么多文件,写得那么绕的感受。

angular2中,RxJS将会是最大的门槛之一。

我以为把,从能解决问题的复杂度上,FRP的确比传统的事件系统高级,可是概念也更多,不是特复杂的程序,这些概念只会增长你的开发难度,而且对后面维护的人要求更高。

Java那么多年,没RxJava,服务器端,android端,那么多年也挺过来了,虽然先进,不必定合适。咱们如今本身的项目,使用的就是本身撸的一个小东西。对咱们如今的规模,开发和维护都挺好的。

相关文章
相关标签/搜索