登录

javascript - 为什么redux中的reducer一定要是纯函数?

如题,为什么会有这样的规定?考虑是什么?如果在reducer里做了其它事情会怎样?

# JavaScript
怪我咯怪我咯2239 天前555 次浏览

全部回复(4) 我要回复

  • 高洛峰

    高洛峰2017-04-11 11:29:12

    这个问题要从很多方面来观察,我用以下的说明来简单解释。

    首先,Redux作者在最初演示时,就有提及它的核心设计的来源有两个:

    Redux = Elm + Flux

    Redux大量借镜了Elm语言的设计逻辑,以及函数编程(functional programming, FP)的编程风格,纯函数是FP中的重要核心,FP是什么?用下面的一句话来说明,摘译自这篇教程文章:

    函数编程就是只使用纯函数与不可改变的值来撰写软体应用的一种方式

    实际上因为React本身也有用到FP的一些设计概念,在官网上有一段对于函数式元件的说明是这样写的,这是在讲解组件对于自己本身props的严格规则。出自官网这里:

    所有的React组件必须运作得就像相对于它们props(属性)的纯函数

    由此看来,Redux虽然是用了纯函数来作为框架的主要内部设计,其实它在某方面只是顺应了React的设计,并且更改进了这些的应用。

    那为何要使用reducer?这要从另一个Redux的组成分子Flux看起。Flux是Facebook中设计出来,为了解决React应用的数据流的一种样式(或架构),细节部不多说。Redux中许多设计,都可以看到Flux架构的影子。理所当然的,Flux是React开发团队或核心周边的应用团队设计的,自然很清楚React的核心是怎么运作,实际上的问题关键在何处,当然也知道怎么让数据流架构起来是最合适的。

    Flux中的数据流运作,最终还是需要对React组件进行state的更动,才能发动重新渲染,而在React中的设计,state不能直接更动,必需透过setState方法,setState方法除了传给它一个要更动的对象值之外,还有一种用法是给它一个函数当传参,公式如下(出自官网的这里):

    function(state, props) => newState

    这公式就是一个reducer函数。React运用了Diff算法,对于目前state与要更动的state进行演算,来达成虚拟DOM的重渲染运作过程。所以,React的算法需要两个值,一个是当前的state,另一个是即将要更动的state值,这样它才能用算法进行演算。

    返回来看Flux中的架构,里面在工具中有一个名称为ReduceStore的东西,Flux的数据核心是名称为store,这个ReduceStore是一个更进化版本的store,它在说明中有一个叫作reduce的方法,说明如下:

    reduce(state: T, action: Object): T 归纳(Reduces)目前的state(状态)与一个action(动作)到新的store中的state(状态)。所有的子类都需要实作这个方法。这个方法必须是纯粹而是无副作用。

    那为何要用这个进化的ReduceStore?它最后有说明一段:

    不需要发送更动事件 注意所有继承自ReduceStore的store,不需要手动发送(emit)在reduce()中的更动事件...state(状态)会自动地比对在每个dispatch(发送)之前与之后,与自动地作发送更动事件...

    ReduceStore的设计与Redux最一开始的版本差不多是同时间发布的,开发者之间彼此有交流。

    以上说明了这么多,回过头来看一下Redux中的reducer与纯函数的必要性。因为这里的说明需要上面的前情提要。

    Redux中的reducer的公式是像下面这样的:

    (previousState, action) => newState

    它长得与React中的setState公式有些相似,与Flux中的ReduceStore中的reduce方法根本就一样。

    所以你可以大概知道,Redux为什么要用纯函数的reducer了,因为Redux原本就是一种Flux架构的改进简化作法,它使用的是ReduceStore类似的机制,可以自动地作一些原本可能很复杂的工作。

    所以在一开始创作Redux时,Redux作者有写一篇博客,谈到他准备要对Flux进行改进的想法,其中就有结论一些方向,例如:

    making the Stores stateless

    这说的Stores无状态就是写成纯函数的reducer。

    Stores and actions are just pure functions. They are easily testable in isolation.

    这里的Stores指的是现在的纯函数的reducer,actions指的是Action Creators。


    最后回覆问题如下:

    为什么会有这样的规定(为什么redux中的reducer一定要是纯函数)?考虑是什么?

    就是为了改进Flux的有些分离不清的架构,简化一些其中的流程。

    不过,这只是其中一种解决方案,但不是唯一的只有一种。只是时至今日,你所看到接受度最高与最热门的是Redux。

    如果在reducer里做了其它事情会怎样?

    目前来说Redux中的reducer必定要是纯函数,如果有副作用写在里面,整个机制运作不起来。

    Redux会报错中断执行,警告里面有出现你用了副作用的代码在reducer里。(所以副作用可以严格检查的…而且有分微量、通常、重度的副作用)

    回复
    0
  • ringa_lee

    ringa_lee2017-04-11 11:29:12

    我的猜测是reducer可以对纯函数做一些优化,比如输入不变时不重复调用

    有副作用也不会怎样 (是否纯函数只是个约定,不是能严格检查的东西) ,就是redux调用的时机未必和你想的一致 (所以不如没有副作用)

    回复
    0
  • 巴扎黑

    巴扎黑2017-04-11 11:29:12

    by design

    回复
    0
  • 天蓬老师

    天蓬老师2017-04-11 11:29:12

    为了方便测试

    回复
    0
  • 取消回复发送