[转] 深入理解React 组件状态(State)

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

一. 如何定义State

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

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

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

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

二. State 与 Props 区别

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

三. 如何正确修改State

1.不能直接修改State。

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

// 错误
this.state.title = ‘React‘;

正确的修改方式是使用setState():

// 正确
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合并多次修改为一次的情况下,相当于等价执行了如下代码:

Object.assign(
  previousState,
  {quantity: this.state.quantity + 1},
  {quantity: this.state.quantity + 1}
)

于是乎,后面的操作覆盖掉了前面的操作,最终购买的数量只增加了1个。

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

// 正确
this.setState((preState, props) => ({
  counter: preState.quantity + 1;
}))

3. State 的更新是一个浅合并(Shallow Merge)的过程。

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

this.state = {
  title : ‘React‘,
  content : ‘React is an wonderful JS library!‘
}

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

this.setState({title: ‘Reactjs‘});

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

{
  title : ‘Reactjs‘,
  content : ‘React is an wonderful JS library!‘
}

四. State与Immutable

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

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

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

this.setState({
  count: 1,
  title: ‘Redux‘,
  success: true
})

2. 状态的类型是数组

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

// 方法一:将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方法:

// 方法一:将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方法:

// 方法一:将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. 状态的类型是普通对象(不包含字符串、数组)

3.1 使用ES6 的Object.assgin方法

// 方法一:将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‘});
}))

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

// 方法一:将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执行的是浅比较(比较对象的引用)。


作者:苍山沭河链接:https://www.jianshu.com/p/c6257cbef1b1來源:简书著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

原文地址:https://www.cnblogs.com/chris-oil/p/8215756.html

时间: 2024-08-27 17:24:01

[转] 深入理解React 组件状态(State)的相关文章

深入理解React 组件状态(State)

React 的核心思想是组件化的思想,应用由组件搭建而成,而组件中最重要的概念是State(状态),State是一个组件的UI数据模型,是组件渲染时的数据依据. 一. 如何定义State 定义一个合适的State,是正确创建组件的第一步.State必须能代表一个组件UI呈现的完整状态集,即组件的任何UI改变,都可以从State的变化中反映出来:同时,State还必须是代表一个组件UI呈现的最小状态集,即State中的所有状态都是用于反映组件UI的变化,没有任何多余的状态,也不需要通过其他状态计算

React.js 基础入门三 ---组件状态state

React 组件实例在渲染的时候创建.这些实例在接下来的渲染中被重复使用,可以在组件方法中通过 this 访问.唯一一种在 React 之外获取 React 组件实例句柄的方式就是保存React.render 的返回值.在其它组件内,可以使用 refs 得到相同的结果(后面会简单解释refs). 从上几章的学习,我们可以这么理解组件,学过php的Yii框架的都知道widget组件,react.js定义一个组件,通过在组件中定义各种'方法','属性'最后通过render来渲染这个组件. 其中<组建

React组件的State

组件state必须能代表一个组件UI呈现的完整状态集,即组件的任何UI改变都可以从state的变化中反映出来:同时,state还必须代表一个组件UI呈现的最小状态集,即state中的所有状态都用于反映组件UI的变化,没有任何多余的状态,也不应该存在通过其他状态计算而来的中间状态. state vs 普通属性 首先,什么是普通属性? 我们的组件都是使用ES6的class定义的,所以组件的属性其实也就是class的属性(更确切的说法是class实例化对象的属性,但因为JavaScript本质上是没有

深入理解React组件传值(组合和继承)

在文章之前,先把这句话读三遍 Props 和组合为你提供了清晰而安全地定制组件外观和行为的灵活方式.注意:组件可以接受任意 props,包括基本数据类型,React 元素以及函数. 来源于React中文文档,组合和继承的一句话 其实这句话之前看过很多遍,主要还是应用于数据获取上. 在完全理解这句话之前,在写子组件改变兄弟子组件的状态上,没有头绪,同事上午跟我讲解了,我自己再把代码重新写一遍就认识到了,我完全忽略了组件传函数的方法,解锁这个方法以后,再写代码如同打开了一扇大门. 下面来看例子: 以

理解React的组件

理解React组件 虽然一直用React写东西,但却不曾从原生去看待react组件的渲染方式,今天就打算深入探讨.有什么理解不对的欢迎指正 功能上看React组件 现在有一个web网站,这个网站可以实现很多功能,我们依据功能,把他们分成一个个小功能碎片,每个小功能碎片需要更多逻辑功能,还可以继续细分.这里React组件就是相当于这样一个个可组合的功能单元,可并列,可嵌套. 从结构上看React组件 创建 一个React组件被创建,包含React nodes和其他react组件.React nod

从 0 到 1 实现 React 系列 —— 组件和 state|props

看源码一个痛处是会陷进理不顺主干的困局中,本系列文章在实现一个 (x)react 的同时理顺 React 框架的主干内容(JSX/虚拟DOM/组件/...) 项目地址 组件即函数 在上一篇 JSX 和 Virtual DOM 中,解释了 JSX 渲染到界面的过程并实现了相应代码,代码调用如下所示: import React from 'react' import ReactDOM from 'react-dom' const element = ( <div className="titl

【react】React学习:状态(State) 和 属性(Props)

React :元素构成组件,组件又构成应用.React核心思想是组件化,其中 组件 通过属性(props) 和 状态(state)传递数据. State 与 Props 区别props 是组件对外的接口,state 是组件对内的接口.组件内可以引用其他组件,组件之间的引用形成了一个树状结构(组件树),如果下层组件需要使用上层组件的数据或方法,上层组件就可以通过下层组件的props属性进行传递,因此props是组件对外的接口.组件除了使用上层组件传递的数据外,自身也可能需要维护管理数据,这就是组件

React: React的组件状态机制

一.简介 在React中,有两个核心的默认属性,分别是state和props.state会记录组件的状态,React根据状态的变化,会对界面做相应的调整或渲染.props则是数据流向属性,React通过props传递来实现父子组件之间的通信.本篇主要研究React的组件状态机制,在很多Web界面可以看到数据不停的变化,其实,这个过程就是React监听到state状态在不停地发生改变时一次次重新对组件重新渲染的结果.基于React这个机制,所以开发者可以很灵活地用state来完成对行为的控制.数据

React之状态(state)与生命周期

很多时候,我们的页面数据是动态的.所以,我们需要实时渲染页面: 一.用定时函数setInterval() 组件(输出当前时间): index.js: 这样每隔1秒页面就会重新渲染一次,这样传进去的时间数据也会更新了. 但是,用setInterval()是有问题的(这里就不作深究),所以引入了下面要说的,组件状态和生命周期概念 注意:以下的内容,你必须用class(类)的方式来编写react组件! 原文地址:https://www.cnblogs.com/eco-just/p/10559427.h