文章标题终于有点正常了……我们已经从上一篇文章中知道了:所谓的“用JavaScript编写HTML代码”React系统指的是React最重要的是React扩展了JavaScript的语法,也就是JSX。React组件可以以类似于HTML语法的方式在JSX语法中使用,这样写React组件就有了创建一个新的HTML标签的体验。上一篇文章《玩转 React(四)- 创造一个新的 HTML 标签》介绍了如何创建React组件及其属性。知道组件的视图是属性的映射,改变组件的属性可以触发组件的重新渲染,从而改变组件的视图。事实上,组件的视图不仅仅是通过属性来映射的。本文将介绍另一种触发组件重新渲染的方式,即组件的内部状态。严格来说,组件的视图是由属性和内部状态组成的。Mapped,即:view=f(props,state),和properties类似,state变化也会触发组件重新渲染,但是state是在组件内部根据自身逻辑或者用户事件维护的,而不是由外部维护的输入。另外,本文将介绍一个类继承定义的组件的生命周期,以及在每个生命周期函数中可以做什么,不可以做什么,不应该做什么。总结ReactDOM.render通常在单页Web应用程序中只被调用一次。组件可以通过setState改变内部状态state来更新视图。setState主要是异步的。不要直接使用当前状态的值生成下一个状态。不要通过this.state直接修改状态。组件生命周期流程图。各生命周期函数介绍及使用心得。以上是对本文内容的总结。如果你已经知道我要说什么,那么就没有必要继续阅读,节省时间。组件的内部状态前面我们了解到,可以通过ReactDOM.render(,container)将具有特定属性的组件渲染到页面的DOM节点(容器)中,这样页面上就会显示“HelloLucy”。当我们想在页面上显示“HelloTom”时,我们可以将组件的name属性改为Tom,然后再次调用ReactDOM.render方法,这样组件就会显示一个新的属性重新渲染,从而更新组件的视图。下面是官方文档中一个时钟显示的例子。我简单修改了一下:https://codepen.io/Sarike/pen...例子中定义了一个Clock组件,该组件接收一个时间属性,通过setInterval传到组件外,周期性调用ReactDOM.render更新Clock的属性并重新渲染。但是在很多实际场景中,对于一个时钟组件,我们希望它有更好的封装性和复用性,也就是说,我们希望只调用一次ReactDOM.render(,container),然后它就可以更新自己自己的视图,让我们更容易在页面上放置多个时钟,也就是复用性更好。为了实现这个目标,需要组件的内部状态来支持它。组件有一个特殊的属性state用来存储组件的内部状态。用户可以通过this.setState(statePatch)更新组件的状态。组件状态更新后,会重新执行render方法更新视图。上面的例子是使用内部状态修改的:https://codepen.io/Sarike/pen。..这样,Clock作为一个完整的时钟组件,就可以进行自我更新了。在上一篇文章中提到,如果要使用组件的内部状态,必须通过类继承来定义组件,而不是使用函数类型的组件。因此,功能组件通常被称为无状态组件(stateless)。在上面的示例中,componentDidMount和componentWillUnmount这两个函数很有用。它们是组件的生命周期函数。本文的后半部分将介绍它们。这两个函数在组件挂载到页面上和组件即将从页面上移除时被调用。.在修改后的例子中,我们只需要调用一次ReactDOM.render即可。在实际项目中,一个完整的单页Web应用只需要调用一次ReactDOM.render,就可以将根组件挂载到页面上。剩下的工作就可以放心地交给React了。初始化组件的内部状态在创建具有内部状态的组件时,我们需要对内部状态进行初始化,即设置组件的初始状态。方法很简单,在构造函数中设置state属性即可。如下所示:classMyComponentextendsReact.Component{constructor(props){super(props);//这行代码不能少this.state={name:"Lucy"}}}setState大部分情况下是异步的setState大部分情况下是异步的。异步是指通过setState更新组件状态后,不能立即通过this.state获取更新后的值。另外,当连续多次调用setState更新同一个字段时,只有最后一次更新才会生效。下面的例子:https://codepen.io/Sarike/pen...如果想让上面的示例代码正常运行,需要通过一个回调函数来产生下一个状态,如下:this.setState(preState=>({value:preState.value+1}));this.setState(preState=>({value:preState.value+2}));this.setState(preState=>({value:preState.value+3}));所以直接根据当前状态的值生成状态是不靠谱的,但是很多不懂这个的同学基本都是这么干的,因为好写,而且好像没什么问题。这是因为很多时候,业务逻辑并没有那么复杂,setState基本不会被频繁调用。但这确实是一个隐患。如果在项目初期不注意规避,当项目复杂到一定程度时,可能会出现难以排查的bug。那么为什么说它在大多数情况下是异步的呢?有些情况不是异步的吗?是的,其实只有React可以控制的事件处理过程中调用的setState是异步的,比如:生命周期函数,React内置的事件处理函数如button、input等组件。大多数情况下我们只需要在这些地方控制我们的组件,所以setState在大多数情况下是异步的。在一些特殊的组件中,可能需要通过addEventListener来设置一些DOM事件处理函数。在这个通过原生JSAPI设置的事件处理过程中,调用setState是同步的,this.state会立即更新。另外,setInterval、setTimeout等原生API的回调函数也是一样的。参考:https://www.zhihu.com/questio...不要直接通过this.state更新组件状态。这类似于属性。直接通过this.state修改组件状态。组件状态被修改,但不会触发组件的重新渲染。这将导致组件视图与状态不一致。生命周期函数一个组件被我们创建到世界中后,它的每一个实例在使用时都有一定的生命周期。下图说明了一个组件实例的生命周期:图片来源:https://tylermcginnis.com/an-...,这张图有点旧,不过下面的没问题。解释一下上图。组件初始化:我们在constructor中定义的每一个组件都是一个类(class),这些类只有在被实例化后才能作为ReactDOM中的一个节点呈现在页面上。因此,当我们第一次通过ReactDOM.render或通过组件中的JSX表达式将组件渲染到页面时,组件首先要做的就是实例化组件。实例化主要是创建一个组件(即Element,通常对应一个JSX表达式,如:)的实例对象。获取组件的默认属性。获取组件的初始内部状态(this.state=xxxx;在构造函数中)。componentWillMount在组件渲染到页面之前执行,并且在组件的整个生命周期中只执行一次。这里可以调用setState来更新内部状态,但是更推荐将状态更新操作放在构造函数中。该函数执行后,会立即执行render方法,将组件渲染到页面上。所以,在这里执行setState不会触发额外的渲染过程,因为这不是必需的。componentDidMount在组件渲染到页面后立即执行,并且在组件的整个生命周期中只执行一次。这是进行以下操作的好时机:一些依赖于组件DOM节点的操作。发出网络请求。设置定时器操作,如setInterval、setTimeout等。这里可以调用setState来更新组件内部状态,会触发一个重新渲染的过程,即重新执行render方法,view会重新渲染更新。componentWillReceivePropscomponentWillReceiveProps(nextProps)这个生命周期函数可能会在两种情况下被调用:组件接收新的属性。新的属性将通过nextProps获取。该组件没有收到新的props,但是由于父组件的重新渲染,当前组件被重新渲染了。只要知道调用函数时,不一定是因为属性发生了变化。这里也可以调用setState来更新组件的内部状态,并且不会触发额外的重新渲染操作。React将巧妙地使用更新后的属性和内部状态执行重新渲染。shouldComponentUpdateshouldComponentUpdate(nextProps,nextState)这是一个查询式的生命周期函数,所以这个函数需要一个返回值true/false,如果为true,组件会触发重新渲染过程,如果为false,组件不会触发重新渲染。因此,合理使用该功能可以在一定程度上节省系统开销,提高系统性能。在这里你不能调用setState来更新组件的状态。由于组件属性或内部状态的改变会触发组件重新渲染,因此该函数接受两个参数:新属性(nextProps)和新状态(nextState)。在处理声明循环函数的时候,记得属性和状态都要同时考虑,不能只考虑一个,不然很容易踩坑。比如:某同学只根据属性判断是否触发重新渲染,而忽略了内部状态,这样无论你怎么设置State,组件视图都无法正常更新。上一篇我们提到类继承来定义组件,React提供了两个基类,一个是Component,一个是PureComponent。两者的区别在于后者已经帮我们简单的实现了shouldComponentUpdate。当属性和状态均未更改时返回false以避免额外开销的函数。但出于性能考虑,比较过程只进行浅比较,即只比较对象的一级字段,是否有变化由Object.is方法类判断。所以有时候组件有变化不更新,但是没有变化就会触发重新渲染过程。在此不再赘述,但想深入探讨一下,大家可以扫描二维码加我微信好友(我的微信:leobaba88)。componentWillUpdate当组件shouldComponentUpdate返回true或调用forceUpdate时,该函数将被触发。您不能在此函数中调用setState来更新组件状态。当你想这样做的时候,你可以考虑把它移到componentWillReceiveProps函数中。该函数在第一次渲染时不会执行。componentDidUpdatecomponentDidUpdate(prevProps,prevState)在重新执行render方法并在组件重新渲染期间更新组件视图后立即执行。类似于组件第一次渲染时的componentDidMount,这个函数不会在第一次渲染时执行。现在是做其中一件事情的好时机:执行依赖于新DOM节点的操作。根据新属性发起新的网络请求。(不过,这里必须格外小心,一定要在确认属性变化后发起网络请求,否则很有可能进入死循环:didUpdate->ajax->changeProps->didUpdate->...).componentWillUnmount在组件从页面中移除之前被调用。这是清理战场的好时机,比如清理定时器、终止网络请求等。componentDidCatchcomponentDidCatch(error,info)这是React16新增的生命周期函数。定义生命周期功能的组件将成为错误边界。errorboundary这个词很形象。它可以有效地将错误限制在一个有限的范围内,而不会导致整个应用程序崩溃,并防止鼠标崩溃。一锅汤。错误边界组件可以捕获在它的整个子组件树中发生的任何异常,但它不能捕获它自己的异常。下面是官方的例子给大家感受一下:https://codepen.io/gaearon/pe...最后(微信群)这篇文章有点慢,非常抱歉。另外,为了方便大家阅读,我会把所有文章的链接更新到第一篇《玩转React(一)- 前言》。毕竟文字表达的范围是有限的。为了方便大家交流,我建了一个微信群。对React感兴趣的同学可以进群一起交流学习。由于微信群邀请时间有限,您可以先扫描下方二维码。加我为好友,我拉大家进群:我的微信:leobaba88