学习React生命周期
React生命周期总体分为三个阶段:创建阶段(Mounting)、运行阶段(Updating)和卸载阶段(Unmounting)。也有种说法分为四个阶段:初始化阶段(Initialization)、创建阶段(Mounting)、运行阶段(Updating)和卸载阶段(Unmounting)
一、React v16.0前的生命周期
1.初始化阶段(Initialization)
也就是下方代码中的类的构造方法,Test类继承Component类,也同时继承这个React的基类(也就继承了render方法和生命周期方法)。
Super是调用基类的构造方法。获取props。
Constructor 构造方法 可初始化state
2.创建阶段(Mounting)
特点:该阶段的函数只执行一次。
(1)ComponentWillMount()
组件被挂在到DOM之前调用,在render()之前被调用,因此在这个方法setState将不会触发页面重新渲染。可以将这里的操作放在constructor()中。(发送ajax请求获取数据);
(2)render()
根据组件中的props和st
4000
ate进行渲染页面(两者的任何变化都会导致页面的重新渲染),返回一个ReactComponent类型的对象,不负责组件的实际渲染工作,之后由react根据此元素去渲染页面DOM。
(注意:不要在render中调用this.setState()否则会造成递归渲染)
React.render的实现是在ReactMount中,源码:
render: function(nextElement, container, callback) { var prevComponent = instancesByReactRootID[getReactRootID(container)]; if (prevComponent) { var prevElement = prevComponent._currentElement; if (shouldUpdateReactComponent(prevElement, nextElement)) { return ReactMount._updateRootComponent( prevComponent, nextElement, container, callback ).getPublicInstance(); } else { ReactMount.unmountComponentAtNode(container); } } var reactRootElement = getReactRootElementInContainer(container); var containerHasReactMarkup = reactRootElement && ReactMount.isRenderedByReact(reactRootElement); var shouldReuseMarkup = containerHasReactMarkup && !prevComponent; var component = ReactMount._renderNewRootComponent( nextElement, container, shouldReuseMarkup ).getPublicInstance(); if (callback) { callback.call(component); } return component; }
如果prevComponent为空也就是第一次进行挂载该ReactElement,直接添加即可,如果已经挂载过会调用shouldUpdateReactComponent(prevElement,nextElement)进行判断对组件进行update还是delete操作。
shouldUpdateReactComponent:如果prevElement类型是string或者number,并且nextElement也为string或者number时候返回true;如果prevElement和nextElement都是对象且key和type属相相同时,则返回prevElement._owner == nextElement._owner。
如果需要更新,则调用ReactMount…_updateRootComponent函数进行Reconciliation,并返回该组件;否则删除该组件,具体操作则是删除container的所有子元素。然后判断shouldReuseMarkup,对于初次挂载的ReactElement而言,该标记为false。最后通过调用_renderNewRootComponent方法将ReactElement渲染到DOM上,并获取对应的ReactComponent对象,最后执行回调并返回组件对象。
对于_renderNewRootComponent方法,通过调用instantiateReactComponent(nextElement, null)来实例化组件,并在ReactMount的缓存中注册组件,批量执行更新ReactUpdates.batchedUpdates,最终通过_mountImageIntoNode方法将虚拟节点插入到DOM中。
(3)ComponentDidMount()
组件被挂载到DOM后且只执行一次;可以获取组件内容的DOM对象并进行DOM操作;可以发送请求获取数据;可以通过this.setState()修改状态的值,修改后会进行页面的重新渲染。
3.运行阶段(Updating)
该阶段的函数执行多次(每当组件的props或者state改变的时候都会触发运行阶段的函数);
(1)ComponentWillReceiveProps(nextProps)
可以通过this.props获取到上一次的值,通过比较this.props和nextProps来判断是否属性发生变化,如果需要响应属性的变化可以在该方法中使用this.setState()处理状态。*在该函数调用this.setState将不会引起二次渲染。
(2)shouldComponentUpdate(nextProps,nextState)
根据这个方法的返回值决定是否重新渲染组件,返回值需布尔值,通过这个方法降低组件渲染的频率提升组件的性能。
(3)ComponentWillUpdate(nextProps,nextState)
组件将要跟新前执行,不能调用setState()否则会造成递归渲染
(4)render()同Mounting阶段的render
(5)ComponentDidUpdate(prevProps,prevState)
组件被跟新后调用,可以操作DOM
4.卸载阶段(Unmounting)
(1)ComponentWillUnmount()
在卸载组件的时候执行清理工作,eg:清理定时器,清除componentDidMount创建的DOM对象。
二、React V16.4生命周期
变更缘由
原来(React v16.0前)的生命周期在React v16推出的Fiber之后就不合适了,因为如果要开启async rendering,在render函数之前的所有函数,都有可能被执行多次。
原来(React v16.0前)的生命周期有哪些是在render前执行的呢?
componentWillMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
如果开发者开了async rendering,而且又在以上这些render前执行的生命周期方法做AJAX请求的话,那AJAX将被无谓地多次调用。。。明显不是我们期望的结果。而且在componentWillMount里发起AJAX,不管多快得到结果也赶不上首次render,而且componentWillMount在服务器端渲染也会被调用到(当然,也许这是预期的结果),这样的IO操作放在componentDidMount里更合适。
禁止不能用比劝导开发者不要这样用的效果更好,所以除了shouldComponentUpdate,其他在render函数之前的所有函数(componentWillMount,componentWillReceiveProps,componentWillUpdate)都被getDerivedStateFromProps替代。
也就是用一个静态函数getDerivedStateFromProps来取代被deprecate的几个生命周期函数,就是强制开发者在render之前只做无副作用的操作,而且能做的操作局限在根据props和state决定新的state
React v16.0刚推出的时候,是增加了一个componentDidCatch生命周期函数,这只是一个增量式修改,完全不影响原有生命周期函数;但是,到了React v16.3,大改动来了,引入了两个新的生命周期函数。
新引入了两个新的生命周期函数:getDerivedStateFromProps,getSnapshotBeforeUpdate
getDerivedStateFromProps
getDerivedStateFromProps本来(React v16.3中)是只在创建和更新(由父组件引发部分),也就是不是不由父组件引发,那么getDerivedStateFromProps也不会被调用,如自身setState引发或者forceUpdate引发。
React v16.3 的生命周期图
React v16.3
这样的话理解起来有点乱,在React v16.4中改正了这一点,让getDerivedStateFromProps无论是Mounting还是Updating,也无论是因为什么引起的Updating,全部都会被调用,具体可看React v16.4 的生命周期图。
React v16.4后的getDerivedStateFromProps
static getDerivedStateFromProps(props, state) 在组件创建时和更新时的render方法之前调用,它应该返回一个对象来更新状态,或者返回null来不更新任何内容。
getSnapshotBeforeUpdate
getSnapshotBeforeUpdate() 被调用于render之后,可以读取但无法使用DOM的时候。它使您的组件可以在可能更改之前从DOM捕获一些信息(例如滚动位置)。此生命周期返回的任何值都将作为参数传递给componentDidUpdate()。
官网给的例子:
class ScrollingList extends React.Component { constructor(props) { super(props); this.listRef = React.createRef(); } getSnapshotBeforeUpdate(prevProps, prevState) { //我们是否要添加新的 items 到列表? // 捕捉滚动位置,以便我们可以稍后调整滚动. if (prevProps.list.length < this.props.list.length) { const list = this.listRef.current; return list.scrollHeight - list.scrollTop; } return null; } componentDidUpdate(prevProps, prevState, snapshot) { //如果我们有snapshot值, 我们已经添加了 新的items. // 调整滚动以至于这些新的items 不会将旧items推出视图。 // (这边的snapshot是 getSnapshotBeforeUpdate方法的返回值) if (snapshot !== null) { const list = this.listRef.current; list.scrollTop = list.scrollHeight - snapshot; } } render() { return ( <div ref={this.listRef}>{/* ...contents... */}</div> ); } }
参考:
程墨Morgan–React v16.3之后的组件生命周期函数
徐超–《React进阶之路》
Aermin–详解react生命周期(包括react16版)
ReactJS分析之入口函数render(网址:https://www.cnblogs.com/accordion/p/4501118.html)
- React 生命周期 学习
- React学习笔记-4-什么是生命周期
- react学习笔记之组件生命周期
- React学习:组件生命周期、组件间数据传递
- React.js学习2——React 组件生命周期、React AJAX、React 表单与事件
- React学习总结—生命周期
- React学习之State与生命周期基友情(四)
- react 学习--组件的生命周期(三)销毁阶段
- 【JAVASCRIPT】React学习-组件生命周期
- react学习(三)之生命周期/refs/受控组件 篇
- react学习小结(生命周期- 实例化时期 - 存在期- 销毁时期)
- 学习React Native 笔记(三)React组件的生命周期
- 【ReactJS】通过一个例子学习React组件的生命周期
- React-Native学习笔记之生命周期
- React Native 学习-组件说明和生命周期
- react学习(6)——关于组件生命周期的问题
- React学习(二):组件的生命周期及数据流
- react学习笔记 item7 --- 组件的生命周期
- React学习-- React源码(3)生命周期的管理艺术
- react-native 生命周期