重生之我又来学React了Day07 -- 不可变数据

128 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情

在react的官方文档,介绍性能优化中有一点叫做不可变数据的力量,那么为什么倡导在react使用不可变数据呢?

首先我们来看看在react中什么叫做不可变数据?当我们修改state,或者props的时候,不是直接修改它原有的值,而且是基于原来的值重新生成一个值,这个过程是不是很熟悉?没错在setState和Redux当中都是这么操作的。

为什么在setState和Redux中要这样操作呢?

setState

该方法是在类组件中用来修改state的方法,只有调用setState对数据做修改视图才会更新。

// 这样视图是不会更新的
this.state.name = "jack";

// 使用setState 视图才会更新
setState({name: "jack"})

image.png 根据上面的生命周期图,因为类组件只会生成一次实例,并且类组件是否更新是根据shouldComponentUpdate这个方法返回的值来看是否执行render方法。一般 shouldComponentUpdate 会比较 props 和 state 中的属性是否发生改变 (浅比较也就是 a===b) 来判定是否返回 true。所以如果直接修改state这个对象的值,作为引用类型直接修改属性浅比较是等于没有修改的,也就不会更新了,而使用useState会重新生成一个引用,前后比较则是不相同的,所以会触发render执行,视图也就更新了。

Redux

Redux是一种数据状态管理的工具库,在Redux文档中,我们可以看到不变性带来的好处。以及为什么Redux要使用不可变数据

简单总结一下就是,为了性能问题,Redux也是使用了浅比较的方式进行数据对比,所以跟useState使用浅比较的思想如出一辙。

当我们编写reducer时

const initialState = { value: 0 }  
  
function counterReducer(state = initialState, action) {  
    // 检查 reducer 是否关心这个 action  
    if (action.type === 'counter/increment') {  
        // 如果是,复制 `state`  
        return {  
        ...state,  
        // 使用新值更新 state 副本  
        value: state.value + 1  
        }  
    }  
    // 返回原来的 state 不变  
    return state  
}

如果直接使用redux,此时数据有修改则需要返回一个新的state对象,而当我们使用Redux-Toolkit的时候,可以直接对state进行操作,因为在createReducer中,它会自动使用 immer库来让你使用普通的 mutable 代码编写更简单的 immutable 更新,例如 state.todos[3].completed = true

immer.js

一个小型的js包,可以让我们更方便的操作不可变数据。

const state = [{title: "React", desc: "我是React"}, {title: "Vue", desc: "我是Vue"}];

// 不使用immer
const newState = [...state]
newState[1] = {
    ...newState[1],
    subTitle: "js"
}
newState.push({title: "Angular"})

// 使用immer
import produce from "immer"

const newState = produce(state, draft => {
    draft[1].subTitle = 'js'
    draft.push({title: "Angular"})
})