为什么Redux 需要 reducers是纯函数?

为什么Redux 需要 reducers是纯函数?

这又是一个很厉害的问题了,使用Redux的都知道,reducers会接收上一个state和action作为参数,然后返回一个新的state,这个新的state不能是在原来state基础上的修改。所以经常可以看到以下的写法:

1
2
3
return Object.assign(...)
//或者----------
return {...state,xx:xxx}

其作用,都是为了返回一个全新的对象。

为什么reducers要求是纯函数(返回全新的对象,不影响原对象)? –某面试官

纯函数

从本质上讲,纯函数的定义如下:不修改函数的输入值,依赖于外部状态(比如数据库,DOM和全局变量),同时对于任何相同的输入有着相同的输出结果。
举个例子,下面的add函数不修改变量a或b,同时不依赖外部状态,对于相同的输入始终返回相同的结果。

1
const add = (a,b) => {a + b};

这就是一个纯函数,结果对a、b没有任何影响,回头去看reducer,它符合纯函数的所有特征,所以就是一个纯函数
为什么必须是纯函数?
先告诉你结果吧,如果在reducer中,在原来的state上进行操作,并返回的话,并不会让React重新渲染。 完全不会有任何变化!
接下来看下Redux的源码:

Redux接收一个给定的state(对象),然后通过循环将state的每一部分传递给每个对应的reducer。如果有发生任何改变,reducer将返回一个新的对象。如果不发生任何变化,reducer将返回旧的state。
Redux只通过比较新旧两个对象的存储位置来比较新旧两个对象是否相同。如果你在reducer内部直接修改旧的state对象的属性值,那么新的state和旧的state将都指向同一个对象。因此Redux认为没有任何改变,返回的state将为旧的state。
好了,也就是说,从源码的角度来讲,redux要求开发者必须让新的state是全新的对象。那么为什么非要这么麻烦开发者呢?
请看下面的例子:尝试比较a和b是否相同

1
2
3
4
5
6
7
8
9
10
11
12
13
var a = {
name: 'jack',
friend: ['sam','xiaoming','cunsi'],
years: 12,
...//省略n项目
}

var b = {
name: 'jack',
friend: ['sam','xiaoming','cunsi'],
years: 13,
...//省略n项目
}

思路是怎样的?我们需要遍历对象,如果对象的属性是数组,还需要进行递归遍历,去看内容是否一致、是否发生了变化。 这带来的性能损耗是非常巨大的。 有没有更好的办法?
有!

1
2
//接上面的例子
a === b //false

我不要进行深度比较,只是浅比较,引用值不一样(不是同一个对象),那就是不一样的。 这就是redux的reducer如此设计的原因了