Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >深入理解React的组件状态

深入理解React的组件状态

作者头像
xiangzhihong
发布于 2022-11-30 03:18:49
发布于 2022-11-30 03:18:49
2.5K00
代码可运行
举报
文章被收录于专栏:向治洪向治洪
运行总次数:0
代码可运行

这几天在阅读徐超老师的《React 进阶之路》,然后在看看自己之前的《React Native移动开发实战》,发现之前我自己的书部分写的比较的浅显,最近打算对基础部分进行升级,加大基础部分,特别是React基础部分的讲解,并对React Native提供的组件部分进行升级。

众所周知,React框架的核心思想是组件化,一个应用程序由多个组件搭建而成,组件最重要的概念是State(状态),State是一个组件的UI数据模型,是组件渲染时的数据依据。

前面一篇文章我们主要介绍了React组件相关的内容,但是对于组件的Props和State并没有做过多的介绍,本文就着重介绍组件的State。

定义State

众所周知,State作为组件的私有属性,主要用于对组件的私有属性进行管理,通过对属性的状态的监听去渲染UI,从而完成用户数据和界面展示的一致性。

定义State是创建组件的第一步,定义的State必须能代表一个组件UI呈现的完整状态集,即组件的任何UI改变,都可以从State的变化中反映出来;同时,State还必须是代表一个组件UI呈现的最小状态集,即State中的所有状态都是用于反映组件UI的变化,没有任何多余的状态,也不需要通过其他状态计算而来的中间状态。

组件中定义的变量是不是应该作为组件State,可以通过下面的4条依据进行判断:

  1. 这个变量是否是通过Props从父组件中获取?如果是,那么它不是一个状态。
  2. 这个变量是否在组件的整个生命周期中都保持不变?如果是,那么它不是一个状态。
  3. 这个变量是否可以通过其他状态(State)或者属性(Props)计算得到?如果是,那么它不是一个状态。
  4. 这个变量是否在组件的render方法中使用?如果不是,那么它不是一个状态。

State 与 Props

除了State, 组件的Props也是和组件的UI展示有关的。他们之间的主要区别是:State是可变的,是组件内部维护的一组用于反映组件UI变化的状态集合;而Props对于使用它的组件来说,是只读的,要想修改Props,只能通过该组件的父组件修改。在组件状态上移的场景中,父组件正是通过子组件的Props, 传递给子组件其所需要的状态。

修改State的正确姿势

1.不能直接修改State。

在React中,直接修改state并不会触发render函数,所以下面的写法是错误的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 错误
this.state.title = 'React';

组件的State只能通过setState()方式进行修改。例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 正确
this.setState({title: 'React'});

2. State 的更新是异步的

调用setState,组件的state并不会立即改变,setState只是把要修改的状态放入一个队列中,React会优化真正的执行时机,并且React会出于性能原因,可能会将多次setState的状态修改合并成一次状态修改。所以不要依赖当前的State,计算下个State。当真正执行状态修改时,依赖的this.state并不能保证是最新的State,因为React会把多次State的修改合并成一次,这时,this.state将还是这几次State修改前的State。另外需要注意的事,同样不能依赖当前的Props计算下个状态,因为Props一般也是从父组件的State中获取,依然无法确定在组件状态更新时的值。

举个例子,对于一个电商类应用,在我们的购物车中,当我们点击一次购买数量按钮,购买的数量就会加1,如果我们连续点击了两次按钮,就会连续调用两次this.setState({quantity: this.state.quantity + 1}),在React合并多次修改为一次的情况下,相当于等价执行了如下代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Object.assign(
  previousState,
  {quantity: this.state.quantity + 1},
  {quantity: this.state.quantity + 1}
)

于是乎,后面的操作覆盖掉了前面的操作,最终购买的数量只增加了1个。如果我们要实现加2的效果,可以使用另一个接收一个函数作为参数的setState,这个函数有两个参数,第一个是当前最新状态(本次组件状态修改后的状态)的前一个状态preState(本次组件状态修改前的状态),第二个参数是当前最新的属性props。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 正确
this.setState((preState, props) => ({
  counter: preState.quantity + 1; 
}))

3. State 的更新是一个浅合并的过程

当调用setState修改组件状态时,只需要传入发生改变的State,而不是组件完整的State,因为组件State的更新是一个浅合并(Shallow Merge)的过程。例如,一个组件的状态为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
this.state = {
  title : 'React',
  content : 'React is an wonderful JS library!'
}

当只需要修改状态title时,只需要将修改后的title传给setState即可。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
this.setState({title: 'Reactjs'});

React会合并新的title到原来的组件状态中,同时保留原有的状态content,合并后的State的内容为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
  title : 'Reactjs',
  content : 'React is an wonderful JS library!'
}

State与Immutable

React官方建议把State当作是不可变对象,一方面是如果直接修改this.state,组件并不会重新render;另一方面State中包含的所有状态都应该是不可变对象。当State中的某个状态发生变化,我们应该重新创建这个状态对象,而不是直接修改原来的状态。那么,当状态发生变化时,如何创建新的状态呢?主要有以下三种情况:

1. 状态的类型是不可变类型(数字,字符串,布尔值,null, undefined)

这种情况最简单,因为状态是不可变类型,直接给要修改的状态赋一个新值即可。例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
this.setState({
  count: 1,
  title: 'Redux',
  success: true
})

2. 状态的类型是数组

如有一个数组类型的状态books,当向books中增加一本书时,使用数组的concat方法或ES6的数组扩展语法(spread syntax)即可。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 方法一:将state先赋值给另外的变量,然后使用concat创建新数组
var books = this.state.books; 
this.setState({
  books: books.concat(['React Guide']);
})

// 方法二:使用preState、concat创建新数组
this.setState(preState => ({
  books: preState.books.concat(['React Guide']);
}))

// 方法三:ES6 spread syntax
this.setState(preState => ({
  books: [...preState.books, 'React Guide'];
}))

当需要从books中截取部分元素作为新状态时,使用数组的slice方法。例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 方法一:将state先赋值给另外的变量,然后使用slice创建新数组
var books = this.state.books; 
this.setState({
  books: books.slice(1,3);
})

// 方法二:使用preState、slice创建新数组
this.setState(preState => ({
  books: preState.books.slice(1,3);
}))

当从books中过滤部分元素后,作为新状态时,使用数组的filter方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 方法一:将state先赋值给另外的变量,然后使用filter创建新数组
var books = this.state.books; 
this.setState({
  books: books.filter(item => {
    return item != 'React'; 
  });
})

// 方法二:使用preState、filter创建新数组
this.setState(preState => ({
  books: preState.books.filter(item => {
    return item != 'React'; 
  });
}))

注意:不要使用push、pop、shift、unshift、splice等方法修改数组类型的状态,因为这些方法都是在原数组的基础上修改,而concat、slice、filter会返回一个新的数组。

3. 状态的类型是普通对象(不包含字符串、数组)

1,使用ES6 的Object.assgin方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 方法一:将state先赋值给另外的变量,然后使用Object.assign创建新对象
var owner = this.state.owner;
this.setState({
  owner: Object.assign({}, owner, {name: 'Jason'});
})

// 方法二:使用preState、Object.assign创建新对象
this.setState(preState => ({
  owner: Object.assign({}, preState.owner, {name: 'Jason'});
}))

2,使用对象扩展语法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 方法一:将state先赋值给另外的变量,然后使用对象扩展语法创建新对象
var owner = this.state.owner;
this.setState({
  owner: {...owner, name: 'Jason'};
})

// 方法二:使用preState、对象扩展语法创建新对象
this.setState(preState => ({
  owner: {...preState.owner, name: 'Jason'};
}))

总结一下,创建新的状态对象的关键是,避免使用会直接修改原对象的方法,而是使用可以返回一个新对象的方法。当然,也可以使用一些Immutable的JS库(如Immutable.js)来实现类似的效果。

那么,为什么React推荐组件的状态是不可变对象呢?一方面是因为不可变对象方便管理和调试;另一方面是出于性能考虑,当对象组件状态都是不可变对象时,我们在组件的shouldComponentUpdate方法中,仅需要比较状态的引用就可以判断状态是否真的改变,从而避免不必要的render调用。当我们使用React 提供的PureComponent时,更是要保证组件状态是不可变对象,否则在组件的shouldComponentUpdate方法中,状态比较就可能出现错误,因为PureComponent执行的是浅比较

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-06-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
React 深入系列3:Props 和 State
文:徐超,《React进阶之路》作者 授权发布,转载请注明作者及出处 ---- React 深入系列3:Props 和 State React 深入系列,深入讲解了React中的重点概念、特性和模式等,旨在帮助大家加深对React的理解,以及在项目中更加灵活地使用React。 React 的核心思想是组件化的思想,而React 组件的定义可以通过下面的公式描述: UI = Component(props, state) 组件根据props和state两个参数,计算得到对应界面的UI。可见,
iKcamp
2018/04/19
2.9K0
React进阶篇(十)性能优化
尽量把style提到组件之外,不要直接写在JSX里面。因为如果style里直接定义样式对象,会导致组件每次渲染都要创建一个新的style对象。
娜姐
2020/09/22
8170
React学习笔记(三)—— 组件高级
下面了的代码,我们用到了数组函数的map方法来实现数组的每一个值变成它的2倍,同时返回一个新数组,最后打印出了这个数组:
张果
2023/03/11
8.4K0
React学习笔记(三)—— 组件高级
【React源码笔记】setState原理解析
点击上方蓝字,发现更多精彩 导语 大家都知道React是以数据为核心的,当状态发生改变时组件会进行更新并渲染。除了通过React Redux、React Hook进行状态管理外,还有像我这种小白通过setState进行状态修改。对于React的初学者来说,setState这个API是再亲切不过了,同时也很好奇setState的更新机制,因此写了一篇文章来进行巩固总结setState。 React把组件看成是一个State Machines状态机,首先定义数值的状态state,通过用户交互后状态发生改变,然
腾讯VTeam技术团队
2021/01/29
2.3K0
React-组件-setState
为什么最终的一个值是1, 不是 3 呢是吧,我明明是进行增加了 3 次加 1 的操作,因为 setState 默认是一个异步的方法, 默认会收集一段时间内所有的更新, 然后再统一更新, 所以就导致了最终的一个值是 1, 不是 3,博主可以大致的提供一下它底层的实现代码这样可以更加的让你对 setState 有更深层次的理解,如下:
杨不易呀
2023/09/30
2090
React篇(025)-我们为什么不能直接更新状态?
正确方法应该是使用 setState( ) 方法。它调度组件状态对象的更新。当状态更改时,组件将会重新渲染。
齐丶先丶森
2022/05/12
1.7K0
React 渲染性能优化
在React内部已经使用了许多巧妙的技术来最小化由于Dom变更导致UI渲染所耗费的时间。对于很多应用来说,使用React后无需太多工作就会让客户端执行性能有质的提升。然而,还是很其他更多的办法来加速React程序。
随风溜达的向日葵
2018/08/07
1.1K0
React 渲染性能优化
Rreact原理
在使用 React.js 的时候,并不需要担心多次进行 setState 会带来性能问题。
用户10169043
2022/11/21
1.1K0
React App 性能优化总结
在 React 内部,React 会使用几项巧妙的小技术,来优化计算更新 UI 时,所需要的最少的更新 DOM 的操作。在大多数情况下,即使你没有针对性能进行专项优化,React 依然很快,但是仍有一些方法可以加速 React 应用程序。本文将介绍一些可用于改进 React 代码的有效技巧。
前端迷
2019/07/19
7.8K0
Redux(一):基本概念
React是一个单向数据流的view层框架,单向数据流、组件化、生命周期是其特点。在React组件关系中,组件状态由自己管理,父子组件通过props传递;兄弟组件那么就需要一个共同的父组件作中转;如果涉及层级比较深的话一层一层传递会非常麻烦。所以大量状态共享是React单独难以解决的问题。
Ashen
2020/06/01
1.3K0
React 组件优化方案
如果你知道在什么情况下你的组件不需要更新,你可以在 shouldComponentUpdate 中返回 false 来跳过整个渲染过程。其包括该组件的 render 调用以及之后的操作。该方法会在 重新渲染前 被触发,其默认实现总是返回 true。
多云转晴
2020/02/17
3.3K0
一天梳理完React所有面试考察知识点
在shouldComponentUpdate()判断中,有一个有意思的问题,解释为什么 React setState() 要用不可变值
beifeng1996
2022/10/06
2.8K0
React中的setState的同步异步与合并
原理可以用这张图来描述,即在react中,setState通过一个队列机制实现state的更新。当执行setState时,会把需要更新的state合并后放入状态队列,而不会立刻更新this.state,当进入组件可更新状态时,这个队列机制就会高效的批量的更新state。
Qwe7
2022/06/09
1.6K0
freeCodeCamp | Front End Development Libraries | 笔记
要创建带有函数的组件,您只需编写一个返回 JSX 或 null 的 JavaScript 函数。需要注意的重要一点是,React 要求你的函数名称以大写字母开头。
yiyun
2023/07/17
7330
freeCodeCamp | Front End Development Libraries | 笔记
第一个React Web应用程序
learn from 《React全家桶:前端开发与实例详解》 https://zh-hans.reactjs.org/tutorial/tutorial.html https://zh-hans.reactjs.org/docs/create-a-new-react-app.html#create-react-app
Michael阿明
2022/07/17
1.1K0
第一个React Web应用程序
React组件的state和props
React的数据是自顶向下单向流动的,即从父组件到子组件中,组件的数据存储在props和state中。实际上在任何应用中,数据都是必不可少的,我们需要直接的改变页面上一块的区域来使得视图的刷新,或者间接地改变其他地方的数据,在React中就使用props和state两个属性存储数据。
WindRunnerMax
2020/12/29
1.5K0
react学习
JSX是一个JavaScript的语法扩展。因为JSX在语法上更简洁JavaScript而不是HTML,所以React DOM使用cameCase来定义属性的名称,而不是用HTML属性名称的命名约定。
Cloud-Cloudys
2020/07/07
4.4K0
揭密React setState
学过react的人都知道,setState在react里是一个很重要的方法,使用它可以更新我们数据的状态,本篇文章从简单使用到深入到setState的内部,全方位为你揭开setState的神秘面纱~
IMWeb前端团队
2019/12/03
1K1
揭密React setState
React基础语法
JSX语法中,可以在大括号内放置任何有效的JavaScript表达式。例如2+2、user.firstName或formatName(user)等均是有效的JavaScript表达式。
前端_AWhile
2020/05/18
5K0
(转) 谈一谈创建React Component的几种方式
原文地址:http://www.cnblogs.com/Unknw/p/6431375.html
mafeifan
2018/09/10
5130
相关推荐
React 深入系列3:Props 和 State
更多 >
领券
💥开发者 MCP广场重磅上线!
精选全网热门MCP server,让你的AI更好用 🚀
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验