React 状态的不变性实例详解

 更新时间:2022年11月13日 11:48:48   作者:何遇er  
这篇文章主要为大家介绍了React 状态的不变性实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

正文

不变性对应的英文单词是 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 状态不变性的资料请关注脚本之家其它相关文章!

相关文章

  • React Streaming SSR原理示例深入解析

    React Streaming SSR原理示例深入解析

    这篇文章主要为大家介绍了React Streaming SSR原理示例深入解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • react-redux的connect与React.forwardRef结合ref失效的解决

    react-redux的connect与React.forwardRef结合ref失效的解决

    这篇文章主要介绍了react-redux的connect与React.forwardRef结合ref失效的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05
  • 详解react-redux插件入门

    详解react-redux插件入门

    这篇文章主要介绍了详解react-redux插件入门,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-04-04
  • React开启代理的2种实用方式

    React开启代理的2种实用方式

    最近有不少伙伴询问react的代理配置,自己也去试验了一下发现不少的问题,在这就将所遇到的心得分享出来,这篇文章主要给大家介绍了关于React开启代理的2种实用方式的相关资料,需要的朋友可以参考下
    2021-07-07
  • 解决React报错You provided a `checked` prop to a form field

    解决React报错You provided a `checked` prop&n

    这篇文章主要为大家介绍了React报错You provided a `checked` prop to a form field的解决方法详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • React 添加引用路径时如何使用@符号作为src文件

    React 添加引用路径时如何使用@符号作为src文件

    这篇文章主要介绍了React 添加引用路径时如何使用@符号作为src文件,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-06-06
  • React利用路由实现登录界面的跳转

    React利用路由实现登录界面的跳转

    这篇文章主要介绍了React利用路由实现登录界面的跳转,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • React服务端渲染和同构的实现

    React服务端渲染和同构的实现

    本文主要介绍了React服务端渲染和同构的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04
  • react-redux中connect的装饰器用法@connect详解

    react-redux中connect的装饰器用法@connect详解

    这篇文章主要介绍了react-redux中connect的装饰器用法@connect详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • React源码state计算流程和优先级实例解析

    React源码state计算流程和优先级实例解析

    这篇文章主要为大家介绍了React源码state计算流程和优先级实例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11

最新评论