第三章 从Flux到Redux(Redux篇)

3.2 Redux

         这本书的主题是关于Redux的,所以我们不要停留在Flux上太久。终于,我们要开始接触Redux了。

        我们把Flux看作一个框架理念的话,Redux是Flux的一种实现,除了Redux之外,还有很多实现Flux的框架,比如Reflux、Fluxible等,毫无疑问Redux获得的关注最多,这不是偶然的,因为Redux有很多框架无法比拟的优势。

3.2.1 Redux的基本原则

        2013年问世的Flux饱受争议,而2015年Dan Abramov提出了在Flux基础上的改进框架Redux,则是一鸣惊人,在所有Flux的变体中算是最受关注的框架,没有之一。

        Flux的基本原则是“单项数据流”,Redux在此基础上强调三个基本原则:

        ⚪ 唯一数据源(Single Source of Truth)

        ⚪ 保持状态只读(State is read-only)

        ⚪ 数据改变只能通过纯函数完成(Changes are with pure functions)

        让我们逐一介绍这三种基本原则。

        1.唯一数据源

        唯一数据源指的是应用的状态数据应该只存储在唯一的一个Store中。

        我们已经知道,在Flux中,应用可以拥有多个Store,往往根据功能把应用的状态数据划分给若干个Store分别存储管理。比如,在上面的CounterPanel例子中,我们创造了CounterStore和SummaryStore。

        如果状态数据分散在多个Store中,容易造成数据冗余,这样数据一致性方面就会出问题。虽然利用Dispatch的waitFor方法可以保证多个Store之间的更新顺序,但是这又产生了不同Store之间的显示依赖关系,这种依赖关系的存在增加了应用的复杂度,容易带来新的问题。

        Redux对这个问题的解决方法就是,整个应用只保持一个Store,所有组件的数据源就是这个Store上的状态。

注意:Redux并没有阻止一个应用拥有多个Store。只是,在Redux的框架下,糖一个应用用于多个Store不会带来任何好处,最后还不如使用一个Store更容易组织代码。

        这个唯Store上的状态,是一个树形的对象,每个组件往往只是树形对象上一部分的数据,而如何设计Store上状态的结构,就是Redux应用的核心问题,我们接下来会描述具体细节。

2.保持状态可读

        保持状态可读,就是说不能去修改状态,要修改Store的状态,必须通过派发一个action对象完成,这一点,和Flux并没有什么区别。

        如果只看这个原则的字面意思,可能会让读者感觉有点费解,还记得那个公式吗?UI=render(state),我们已经说过驱动用户界面更改的是状态,如果状态都是只读的不能修改,怎么可能引起用户界面的变化呢?

·        当然,要驱动用户界面渲染,就要改变应用的状态,但是改变状态的方法不是去修改状态上的值,而是创建一个新的状态对象返回给Redux,由Redux完成新的状态的组装。

        这就直接引出了下面的第三个基本原则。

3.数据改变只能通过纯函数完成

        这里所说的纯函数就是Reducer,Redux这个名字的前三个字母Red代码的就是Reducer。按照创作者Dan Abramov的说法,Redux名字的含义是Reducer+Flux。

        Reducer不是一个Redux特定的术语,而是一个计算机科学的通过概念,很多语言和框架都有对Reducer函数的支持。就以JavaScript为例,数组类型就有reduce函数,接受的参数就是一个reducer,reduce做的事情就是把数组所有元素依次做“规约”,对每个元素都调用一次参数reducer,通过reducer函数完成规约所有元素的功能。

        下面是一个使用reducer函数的例子:

        [1,2,3,4].reduce(function reducer(accumulation,item){

        return accumulation+item

    },0);

        上面的代码中,reducer(注意不是reduce)函数接受两个参数,第一个参数是上一次规约的结果,第二个参数是这一次规约的元素,函数体是返回两者之和,所以这个规约的结果就是所有元素之和。

        在Redux中,每个reducer的函数签名如下所示:

        reducer(state,action)

        第一个参数state是当前的状态,第二个参数action是接受到的action对象,而reducer函数要做的事情,就是根据state和action的值产生一个新的对象返回,注意reducer必须是纯函数,也就是说函数的返回结果必须完全由参数state和action决定,而且不产生任何副作用,也不能修改参数state和action对象。

        让我们回顾一下Flux中的Store是如何处理函数的,代码如下:

        CounterStore.dispatchToken = AppDispatcher.register((action)=>{

            if(action.type ===ActionType.INCREMENT){

            counterValues[action.counterCaption]++;

            CounterStore.emitChange();

        }else if(action.type ===ActionTypes.DECREMENT){

          counterValues[action.counterCaption]--;

            CounterStore.emitChange();

        }

    });

        Flux更新状态的函数只有一个参数action,因为状态是由Store直接管理的,所以处理函数中会看到代码直接更新state;在Redux中,一个实现同样功能的reducer代码如下:

        function reduce(state,action)=>{

        const {counterCaption} = action;

        siwtch(action.type){

            case ActionTypes.INCREMENT:

            return {...state,[counterCaption]:state[counterCaption]+1};

        case ActionTypes.DECREMENT:

            return {...state,[counterCaption]:state[counterCaption]-1};

        }

    }

        可以看到reducer函数不光接受action为参数,还接受state为参数。也就是说,Redux的reducer只负责计算状态,却并不负责存储状态。

        我们会在后面的实例中详细解释这个reducer的构造。

        读到这里,读者可能会有一个疑问,从Redux的基本原则来看,Redux并没有赋予我们更强大的功能,反而是给开发者增加了很多限制啊,开发者丧失了想怎么写就怎么写的灵活度。而:增加限制却正是提高软件质量的法门。

3.2.2 Redux实例

        单纯只看书面介绍难以理解Redux如何工作的,让我们还是通过例子来介绍。

        前面我们用Flux实现了一个ControlPanel的应用,接下来让我们用Redux来重新实现一遍同样的功能,通过对比查看二者的差异。

        React和Redux事实上是两个独立的产品,一个应用可以使用React而不使用Redux,也可以使用Redux而不使用React,但是,如果两者结合使用,没有理由不使用一个名叫react-redux的库,这个库能够大大简化代码的书写。

        不过,如果一开始就使用react-redux,可能对其设计思路一头雾水,所以,我们的实例先不采用react-redux库,而从最简单的Redux使用方法开始,初步改进,循序渐进地过渡到使用react-redux。

        最基本的Redux实现,存在于本书对于Github的chapter-03/redux_basic目录中,在这里我们只关注使用Redux实现和使用Flux实现的不同的文件。

未完待续。



        

你可能感兴趣的:(第三章 从Flux到Redux(Redux篇))