react项目中,在reducer里直接更改state是无法触发更新的
1 | reducers: { |
因为reducer 是一个有(previousState, action) => newState
特征的纯函数,接受一个action,和现有的state一起计算出并返回新的state,如上代码更改state,react不会比较这个state是否和之前的state数据不同,而是认为state没有发生变化从而不会触发重新渲染。
应写作:
1 | reducers: { |
*这是一种浅拷贝方式,之后会详细理解深浅拷贝,和用immutable data的意义。
###为什么reducer必须是一个纯函数?
首先纯函数要求相同的输入必须获得相同的输出,如Js Array的splice就不是一个纯函数,这个方法是从Array中删除指定的某些数据,不会返回一个新的数组,而是直接在原数据上修改,可想而知,如果原数据发生了改变,那么相同的输入一定不会有相同的结果。
这样直接修改原数据(引用所指向的值)叫做突变(mutate),是一种不鼓励的做法
它通常会打乱调试的过程,以及 React Redux 的 connect 函数:
- 对于调试过程, Redux DevTools 期望重放 action 记录时能够输出 state 值,而不会改变任何其他的状态。突变或者异步行为会产生一些副作用,可能使调试过程中的行为被替换,导致破坏了应用。
- 对于 React Redux connect 来说,为了确定一个组件(component)是否需要更新,它会检查从 mapStateToProps 中返回的值是否发生改变。为了提升性能,connect 使用了一些依赖于不可变 state 的方法。并且使用浅引用(shallow reference)来检测状态的改变。这意味着直接修改对象或者数组是不会被检测到的并且组件不会被重新渲染。
其他的副作用像在 reducer 中生成唯一的 ID 或者时间戳时也会导致代码的不可预测并且难以调试和测试。
因此我们便可以理解为什么第一种写法无法触发更新,mapStateToProps
的浅引用只会判断该引用地址有没有发生改变,所以正确的做法是返回一个新的state,而不是在原有state上修改数据
###引用和传递方式
js的数据类型分为基本类型和引用类型
- 基本类型:6 种基本数据类型, Undefined 、 Null 、 Boolean 、 Number 、 String 、 Symbol
- 引用类型:统称为 Object 类型,细分为:Object 类型、 Array 类型、 Date 类型、 RegExp 类型、 Function 类型等。
其中基本类型的传递方式是按值传递的,修改形参不会改变实参的值,即传入的是参数的拷贝。同时在复制一个基本类型的时候会重新分配一个空间,修改复制后的新变量不会影响原始数据。
而引用类型是指向数据的内存地址,引用类型在作为参数传入函数时,使用共享传递(call by sharing),该策略的重点是:调用函数传参时,函数接受对象实参引用的副本(既不是按值传递的对象副本,也不是按引用传递的隐式引用)。
这样的传递方式, 由于传入的是引用的副本,我们直接对形参复制不会影响实参的值
1 | var obj={a:1} |
而由于引用和其副本都指向同一块内存区域,所以修改形数的属性,实参的也会发生改变
1 | var obj={a:1} |
###深拷贝和浅拷贝
深拷贝和浅拷贝是引用类型(对象类型)才有的概念
基本类型:如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。
而引用类型的直接复制是浅拷贝,a = b
, b只是拷贝了a的引用,而没有新建一个内存空间,也就是说a和b指向同一个数据内存,这样的拷贝弊端是 如果我们修改副本b的数据,原数据a也会改变,这样就造成前文所说的突变。
而深拷贝就是为b分配新的内存中,将a的各个属性都复制到新内存里,
也就是说,当b中的属性有变化的时候,a内的属性不会发生变化。
####浅拷贝
- 直接复制
- Object.assign()
”Object.assign() 方法用于将所有可枚举的属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。”
1 | const object1 = { |
我们修改副本的a为100, 原对象的a也变成了100