React 状态的不变性实例详解

 更新时间:2022年11月14日 09:30  点击:1163 作者:何遇er

正文

不变性对应的英文单词是 Immutability,它不是 React 中的概念,但它对写一个正确的 React 程序至关重要。不考虑生命周期函数 shouldComponentUpdate 对组件重新渲染造成的影响,当组件的 state 发生变化时,组件将被重新渲染。

你可曾遇到过这样一种情况——你自认为改变了 state 的值,但是组件没有重新渲染?本文将揭露其中的缘由并介绍怎么编写符合 Immutability 原则的代码。

什么是 immutable

immutable 指不发生变化,这意味着创建新的值去替换原来的值,而非改变原来的值,与 immutable 相反的概念是 mutable,下面用代码演示 mutable 和 immutable。

let user = {name: 'Bela'}
user.name = 'CI' // 改变user的name属性
user.age = 12 // 给user新增age属性
user = {name: 'Bela'} // 用新的值替换原来的值

上述代码第 2 行和第 3 行都属于改变原来的值,只有第四行是新建一个对象,用新对象替换原来的对象。下面调用函数进一步说明 immutable 与 mutable。

function addAgeMutable(user: User) {
    user.age = 12 // 修改原来的
    return user
}
function addAgeImmutable(user: User) {
    const other = Object.assign({}, user) // 创建新的
    other.age = 12
    return other
}
let user1Original = {name: 'Bella'}
let user1New = addAgeMutable(user1Original) // 用 mutable 的方式
let user2Original = {name: 'Bella'}
let user2New = addAgeImmutable(user2Original) // 用 immutable 的方式
console.log('user1Original 与 user1New 相同吗?',user1Original === user1New) // true
console.log('user2Original 与 user2New 相同吗?',user2Original === user2New) // false

上述 addAgeMutable 函数直接在入参上新增 age 属性,但 addAgeImmutable 函数没有改变入参,而是新建了一个对象,在新对象上添加age属性。

总结一下,immutable 是指不修改原来的;mutable 是指在原来的基础上修改。通过 mutable 的方式修改变量会导致修改前后变量的引用不变。某些操作数组的方法会让原来的数组发生变化,比如:push/pop/shift/unshift/splice,这些方法是 mutable 的,而有一些操作数组的方法不会让原来的数组发生变化,而是返回一个新组件,比如:slice/concat,这些函数是 immutable 的。字符串、布尔值和数值操作都不改变原来的值,而是创建一个新的值。

React 与 Immutability

在 React 程序中,组件的 state 必须具备不变性,接下来演示修改state的正确与不正确的方式。为了说明state的组成结构,先定义个State接口,代码如下:

interface State {
  user: User
  hobbies: string[]
  time: string
}

从上述接口可以看出,组件有三个状态,分别为:user、hobbies 和 time,它们的数据类型各不相同。

修改 state 的错误案例

下面罗列的案例试图用 mutable 的方式修改 state,这些做法全部是错误的。

// 案例一
this.state.user.age = 13
// 案例二
this.setState({
    user: Object.assign(this.state.user, {age: 13})
})
// 案例三
this.setState({
    hobbies: this.state.hobbies.reverse(),
})
// 案例四
this.state.hobbies.length = 0
this.setState({
    hobbies: this.state.hobbies,
})

  • 案例一: 直接修改 user 的内部结构,修改前后 user 的引用不变。
  • 案例二: 错误使用 Object.assign,Object.assign 将第二个参数的属性合并到第一个参数上,然后将第一个参数返回,这意味着案例二还是修改了user的内部结构,修改前后user的引用不变。
  • 案例三: 使用reverse将数组翻转,它翻转的是原数组,翻转前后数据的引用不变。
  • 案例四: 修改hobbies的长度,修改前后hobbies的引用一样。

上述四个案例都不符合数据一旦创建就不发生变化的原则,由于调用了 setState 方法,所以对于用 React.Component 创建的组件而言,不会发生故障,对于用 React.PureComponent 创建的组件,会引发故障,即:界面不更新。

修改 state 的正确案例

下面罗列的案例与错误案例一一对应,它们通过 immutable 的方式修改 state。

    // 案例一
    this.setState({
        user: {...this.state.user, age: 13}
    })
    // 案例二
    this.setState({
        user: Object.assign({},this.state.user, {age: 13})
    })
    // 案例三
    this.setState({
        hobbies: [...this.state.hobbies].reverse()
    })
    // 案例四
    this.setState({
        hobbies: []
    })

上述案例都是新建一个值,用新的值替换原来的值,符合数据一旦创建就不发生变化的原则。

总结

在 react 应用中,更新 state 必须满足 Immutability 原则,因为 React.memo、PureComponent shouldComponentUpdate 和 React Hooks 通过浅比较确定 state 是否发生变更,如果变更 state 的方式不满足 Immutability 原则,它们会认为 state 的值没有变化。

在更新 state 并重新渲染时,React 会将类组件的 this.setState 与函数组件的 useState、useReducer hooks 区别对待。在函数组件中,React 要求所有 hooks 更新状态必须返回一个新的引用作为状态值,如果 React 发现状态更新来自 hook,它会检查该值的引用是否与以前的引用相同,如果相同,它将退出该函数组件的渲染流程,最终用户界面不更新。使用 this.setState 更新类的 state,React 并不关心状态的引用是否变化,只要在类组件中调用 this.setState,该组件一定会重新渲染。

以上就是React 状态的不变性实例详解的详细内容,更多关于React 状态不变性的资料请关注猪先飞其它相关文章!

原文出处:https://juejin.cn/post/7165338973806526501

[!--infotagslink--]

相关文章

  • 关于React Native报Cannot initialize a parameter of type'NSArray<id<RCTBridgeModule>>错误(解决方案)

    这篇文章主要介绍了关于React Native报Cannot initialize a parameter of type'NSArray<id<RCTBridgeModule>>错误,本文给大家分享解决方案,需要的朋友可以参考下...2021-05-12
  • 分享一段php获取linux服务器状态的代码

    简单的php获取linux服务器状态的代码,不多说-直接上函数:复制代码 代码如下:function get_used_status(){ $fp = popen('top -b -n 2 | grep -E "^(Cpu|Mem|Tasks)"',"r");//获取某一时刻系统cpu和内存使用情况 $rs =...2014-05-31
  • React引入antd-mobile+postcss搭建移动端

    本文给大家分享React引入antd-mobile+postcss搭建移动端的详细流程,文末给大家分享我的一些经验记录使用antd-mobile时发现我之前配置的postcss失效了,防止大家踩坑,特此把解决方案分享到脚本之家平台,需要的朋友参考下吧...2021-06-21
  • React使用高德地图的实现示例(react-amap)

    这篇文章主要介绍了React使用高德地图的实现示例(react-amap),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-04-18
  • 使用 React 和 Threejs 创建一个VR全景项目的过程详解

    这篇文章主要介绍了使用 React 和 Threejs 创建一个VR全景项目的过程详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-04-06
  • React+高德地图实时获取经纬度,定位地址

    思路其实没有那么复杂,把地图想成一个盒子容器,地图中心点想成盒子中心点;扎点在【地图中心点】不会动,当移动地图时,去获取【地图中心点】经纬度,设置某个位置的时候,将经纬度设置为【地图中心点】即可...2021-06-20
  • React列表栏及购物车组件使用详解

    这篇文章主要为大家详细介绍了React列表栏及购物车组件使用,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-06-28
  • react使用antd表单赋值,用于修改弹框的操作

    这篇文章主要介绍了react使用antd表单赋值,用于修改弹框的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-10-29
  • 教你如何监控 Java 线程池运行状态的操作(必看)

    这篇文章主要介绍了教你如何监控 Java 线程池运行状态的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-27
  • React Native 启动流程详细解析

    这篇文章主要介绍了React Native 启动流程简析,文以 react-native-cli 创建的示例工程(安卓部分)为例,给大家分析 React Native 的启动流程,需要的朋友可以参考下...2021-08-18
  • 一百多行代码实现react拖拽hooks

    这篇文章主要介绍了一百多行代码实现react拖拽hooks,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-21
  • 示例详解react中useState的用法

    useState 通过在函数组件里调用它来给组件添加一些内部 state,React 会在重复渲染时保留这个 state,接下来通过一个示例来看看怎么使用 useState吧...2021-06-04
  • React 高阶组件HOC用法归纳

    高阶组件就是接受一个组件作为参数并返回一个新组件(功能增强的组件)的函数。这里需要注意高阶组件是一个函数,并不是组件,这一点一定要注意,本文给大家分享React 高阶组件HOC使用小结,一起看看吧...2021-06-13
  • 详解bash中的退出状态机制

    这篇文章主要介绍了详解bash中的退出状态机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-07-11
  • React中使用setInterval函数的实例

    这篇文章主要介绍了React中使用setInterval函数的实例,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-04-06
  • 关于antd tree和父子组件之间的传值问题(react 总结)

    这篇文章主要介绍了关于antd tree 和父子组件之间的传值问题,是小编给大家总结的一些react知识点,本文通过一个项目需求实例代码详解给大家介绍的非常详细,需要的朋友可以参考下...2021-06-02
  • react为什么不推荐使用index作为key

    本文主要介绍了react为什么不推荐使用index作为key,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-07-22
  • react自动化构建路由的实现

    这篇文章主要介绍了react自动化构建路由的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-04-23
  • React Class组件生命周期及执行顺序

    这篇文章主要介绍了React Class组件生命周期,包括react组件的两种定义方式和class组件不同阶段生命周期函数执行顺序,本文给大家介绍的非常详细,需要的朋友可以参考下...2021-08-14
  • react+ts实现简单jira项目的最佳实践记录

    这篇文章主要介绍了react+ts实现简单jira项目,本文通过图文实例相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-07-30