Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >React 深入系列3:Props 和 State

React 深入系列3:Props 和 State

作者头像
iKcamp
发布于 2018-04-19 08:58:54
发布于 2018-04-19 08:58:54
2.8K00
代码可运行
举报
文章被收录于专栏:iKcampiKcamp
运行总次数:0
代码可运行

文:徐超,《React进阶之路》作者

授权发布,转载请注明作者及出处


React 深入系列3:Props 和 State

React 深入系列,深入讲解了React中的重点概念、特性和模式等,旨在帮助大家加深对React的理解,以及在项目中更加灵活地使用React。

React 的核心思想是组件化的思想,而React 组件的定义可以通过下面的公式描述:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
UI = Component(props, state)

组件根据props和state两个参数,计算得到对应界面的UI。可见,props 和 state 是组件的两个重要数据源。

本篇文章不是对props 和state 基本用法的介绍,而是尝试从更深层次解释props 和 state,并且归纳使用它们时的注意事项。

Props 和 State 本质

一句话概括,props 是组件对外的接口,state 是组件对内的接口。组件内可以引用其他组件,组件之间的引用形成了一个树状结构(组件树),如果下层组件需要使用上层组件的数据或方法,上层组件就可以通过下层组件的props属性进行传递,因此props是组件对外的接口。组件除了使用上层组件传递的数据外,自身也可能需要维护管理数据,这就是组件对内的接口state。根据对外接口props 和对内接口state,组件计算出对应界面的UI。

组件的props 和 state都和组件最终渲染出的UI直接相关。两者的主要区别是:state是可变的,是组件内部维护的一组用于反映组件UI变化的状态集合;而props是组件的只读属性,组件内部不能直接修改props,要想修改props,只能在该组件的上层组件中修改。在组件状态上移的场景中,父组件正是通过子组件的props,传递给子组件其所需要的状态。

如何定义State

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

组件中用到的一个变量是不是应该作为组件state,可以通过下面的4条依据进行判断:

  1. 这个变量是否是通过props从父组件中获取?如果是,那么它不是一个状态。
  2. 这个变量是否在组件的整个生命周期中都保持不变?如果是,那么它不是一个状态。
  3. 这个变量是否可以通过state 或props 中的已有数据计算得到?如果是,那么它不是一个状态。
  4. 这个变量是否在组件的render方法中使用?如果不是,那么它不是一个状态。这种情况下,这个变量更适合定义为组件的一个普通属性(除了props 和 state以外的组件属性 ),例如组件中用到的定时器,就应该直接定义为this.timer,而不是this.state.timer。

请务必牢记,并不是组件中用到的所有变量都是组件的状态!当存在多个组件共同依赖同一个状态时,一般的做法是状态上移,将这个状态放到这几个组件的公共父组件中。

如何正确修改State
1.不能直接修改State。

直接修改state,组件并不会重新重发render。例如:

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

正确的修改方式是使用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。另外需要注意的是,同样不能依赖当前的props计算下个state,因为props的更新也是异步的。

举个例子,对于一个电商类应用,在我们的购物车中,当点击一次购买按钮,购买的数量就会加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个。

如果你真的有这样的需求,可以使用另一个接收一个函数作为参数的setState,这个函数有两个参数,第一个参数是组件的前一个state(本次组件状态修改成功前的state),第二个参数是组件当前最新的props。如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 正确
this.setState((preState, props) => ({
  counter: preState.quantity + 1; 
}))
3. State 的更新是一个浅合并(Shallow Merge)的过程。

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

代码语言: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到原来的组件state中,同时保留原有的状态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)

这种情况最简单,因为状态是不可变类型,直接给要修改的状态赋一个新值即可。如要修改count(数字类型)、title(字符串类型)、success(布尔类型)三个状态:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
this.setState({
  count: 1,
  title: 'Redux',
  success: true
})
2. 状态的类型是数组

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 方法一:使用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代码解释
复制
// 使用preState、slice创建新数组
this.setState(preState => ({
  books: preState.books.slice(1,3);
}))

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 使用preState、filter创建新数组
this.setState(preState => ({
  books: preState.books.filter(item => {
    return item != 'React'; 
  });
}))

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

3. 状态的类型是简单对象(Plain Object)

如state中有一个状态owner,结构如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
this.state = {
  owner = {
    name: '老干部',
    age: 30
  }  
}

当修改state时,有如下两种方式:

1) 使用ES6 的Object.assgin方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
this.setState(preState => ({
  owner: Object.assign({}, preState.owner, {name: 'Jason'});
}))

2) 使用对象扩展语法(object spread properties

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
this.setState(preState => ({
  owner: {...preState.owner, name: 'Jason'};
}))

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

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

下篇预告:

React 深入系列4:组件的生命周期

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

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

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

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
深入理解React的组件状态
这几天在阅读徐超老师的《React 进阶之路》,然后在看看自己之前的《React Native移动开发实战》,发现之前我自己的书部分写的比较的浅显,最近打算对基础部分进行升级,加大基础部分,特别是React基础部分的讲解,并对React Native提供的组件部分进行升级。
xiangzhihong
2022/11/30
2.5K0
React进阶篇(十)性能优化
尽量把style提到组件之外,不要直接写在JSX里面。因为如果style里直接定义样式对象,会导致组件每次渲染都要创建一个新的style对象。
娜姐
2020/09/22
8170
React学习笔记(三)—— 组件高级
下面了的代码,我们用到了数组函数的map方法来实现数组的每一个值变成它的2倍,同时返回一个新数组,最后打印出了这个数组:
张果
2023/03/11
8.4K0
React学习笔记(三)—— 组件高级
React组件的state和props
React的数据是自顶向下单向流动的,即从父组件到子组件中,组件的数据存储在props和state中。实际上在任何应用中,数据都是必不可少的,我们需要直接的改变页面上一块的区域来使得视图的刷新,或者间接地改变其他地方的数据,在React中就使用props和state两个属性存储数据。
WindRunnerMax
2020/12/29
1.5K0
Rreact原理
在使用 React.js 的时候,并不需要担心多次进行 setState 会带来性能问题。
用户10169043
2022/11/21
1.1K0
react --- React中state和props分别是什么?
组件从概念上看就是一个函数,可以接受一个参数作为输入值,这个参数就是props,所以可以把props理解为从外部传入组件内部的数据。由于React是单向数据流,所以props基本上也就是从服父级组件向子组件传递的数据。
小蔚
2019/08/27
8200
【React源码笔记】setState原理解析
点击上方蓝字,发现更多精彩 导语 大家都知道React是以数据为核心的,当状态发生改变时组件会进行更新并渲染。除了通过React Redux、React Hook进行状态管理外,还有像我这种小白通过setState进行状态修改。对于React的初学者来说,setState这个API是再亲切不过了,同时也很好奇setState的更新机制,因此写了一篇文章来进行巩固总结setState。 React把组件看成是一个State Machines状态机,首先定义数值的状态state,通过用户交互后状态发生改变,然
腾讯VTeam技术团队
2021/01/29
2.2K0
React基础(6)-React中组件的数据-state
一个组件最终渲染的数据结果,除了prop还有state,state代表的是当前组件的内部状态,你可以把组件看成一个'状态机",它是能够随着时间变化的数据,更多的是应当在实现交互时使用,根据状态state的改变呈现不同的UI展示
itclanCoder
2019/11/04
6.3K0
深入挖掘React中的state
针对react中对于FunctionComponet,ClassComponent,DOM节点的基本处理和挂载已经告一段落了。
19组清风
2021/11/15
4360
React State(状态): React通过this.state来访问state,通过this.setState()方法来更新stateReact State(状态)
React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。 React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。 以下实例中创建了 LikeButton 组件,getInitialState 方法用于定义初始状态,也就是一个对象,这个对象可以通过 this.state 属性读取。当用户点击组件,导致状态变化,this.setState 方法就修改状态值,每次修改以后,自动调用 this.render 方法,再次渲染组件。
一个会写诗的程序员
2018/08/17
2K0
渐进式React源码解析--State源码
文章中涉及到的知识都是渐进式的讲解开发,当然如果对之间内容不感兴趣(已经了解),也可以直接切入本文内容,每一个章节都和之前不会有很强的耦合。
19组清风
2021/11/15
7910
渐进式React源码解析--State源码
React中的State与Props
一个组件的显示形态可以由数据状态和外部参数决定,其中,数据状态为 state,外部参数为 props
Leophen
2019/08/23
6810
React中的State与Props
React App 性能优化总结
在 React 内部,React 会使用几项巧妙的小技术,来优化计算更新 UI 时,所需要的最少的更新 DOM 的操作。在大多数情况下,即使你没有针对性能进行专项优化,React 依然很快,但是仍有一些方法可以加速 React 应用程序。本文将介绍一些可用于改进 React 代码的有效技巧。
前端迷
2019/07/19
7.8K0
react学习
JSX是一个JavaScript的语法扩展。因为JSX在语法上更简洁JavaScript而不是HTML,所以React DOM使用cameCase来定义属性的名称,而不是用HTML属性名称的命名约定。
Cloud-Cloudys
2020/07/07
4.4K0
从 0 到 1 实现 React 系列 —— 组件和 state|props
看源码一个痛处是会陷进理不顺主干的困局中,本系列文章在实现一个 (x)react 的同时理顺 React 框架的主干内容(JSX/虚拟DOM/组件/...)
牧云云
2018/08/02
7720
从 0 到 1 实现 React 系列 —— 组件和 state|props
一天梳理完React所有面试考察知识点
在shouldComponentUpdate()判断中,有一个有意思的问题,解释为什么 React setState() 要用不可变值
beifeng1996
2022/10/06
2.8K0
React基础语法
JSX语法中,可以在大括号内放置任何有效的JavaScript表达式。例如2+2、user.firstName或formatName(user)等均是有效的JavaScript表达式。
前端_AWhile
2020/05/18
5K0
React 中的useState 和 setState 的执行机制
useState 和 setState 在React开发过程中 使用很频繁,但很多人都停留在简单的使用阶段,并没有正在了解它们的执行机制,例如:「它们是同步的还是异步的?」 正因为没有理解它们,才致使开发过程中会碰到一些出乎意料的bug。本文将带大家了解它们的特性。
用户8921923
2022/10/24
3.3K1
React三大属性之一 state的一些简单的理解
React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。组件的任何UI改变,都可以从State的变化中反映出来;State中的所有状态都用于反映UI的变化,不应有多余状态。
henu_Newxc03
2021/12/26
1.4K0
前端框架与库 - React基础:组件、Props、State
React 是一个用于构建用户界面的 JavaScript 库,特别擅长创建可复用的组件。掌握组件、Props 和 State 的概念是学习 React 的基石。本文将深入浅出地探讨这些核心概念,包括常见问题、易错点以及如何避免这些问题,并附带代码示例。
Jimaks
2024/07/12
1550
相关推荐
深入理解React的组件状态
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验