react的生命周期(二)
前面已经详细讲解过react的生命周期,具体可以参考我的另一篇文章:https://www.jianshu.com/p/4be358fadb3d
React组件生命周期有三个阶段:加载、更新和卸载。每个阶段有多个方法来调用实现某些功能。这些方法名字也很有意思,带will前缀表示在该阶段发生之前调用,did表示在该阶段发生之后调用。本文将介绍这些方法。本文翻译自React官网文档,如有翻译不当,请不吝指正。
1. Mounting阶段:
该阶段表示一个组件实例被创建并被插入到DOM中。该阶段有四个方法:constructor(),componentWillMount(),render()和componentDidMount()。
constructor(props):
该方法在组件加载前调用。当组件作为React.Component的子类实现时需要在其他声明之前先调用super(props)。否则,this.props在构造器中会是undefined,这会导致代码出现bug。
作用:
用来初始化状态,如果既不初始化状态也不绑定方法,那就没必要实现该方法了。(笔者注:事实上,如果组件没有构造器方法的话,组件会默认调用一个构造器,实现属性的传递)。
componentWillMount():
该方法会在加载发生前调用。因为它发生在render()方法前,因此在该方法内同步设置状态不会引发重渲染。该方法还是服务器端渲染的唯一的生命周期钩子。一般,推荐用constructor()代替该方法。
render():
该方法必须要有。当调用时,它会检查this.props和this.state,然后返回一个React元素。这个元素可以是原生DOM组件如div,也可以是一个自定义的复合组件。如果什么也不想渲染的话,可以返回null或false。当返回null或false时,ReactDOM.findDOMNode(this)会返回null。该方法应该是纯净的,这意味着它不能修改组件状态,每次调用会返回相同的结果,不会和浏览器发生直接的交互。如果想要同浏览器发生交互的话,应该在componentDidMount()中实现。
componentDidMount():组件加载完成后触发。
作用:
放置必要的DOM节点。如果要从远程节点加载数据,这是一个不错的实例化网络请求的地方。也可以在此处设置定时器等。
注意:在该方法内设置状态会导致重渲染。
2. Updating阶段:
该阶段表示由状态或属性的改变导致组件的重渲染。该阶段有五个方法:componentWillReceiveProps(),shouldComponentUpdate(),componentWillUpdate(),render()和componentDidUpdate()。
componentWillReceiveProps(nextProps):
该方法会在加载好的组件在收到新的状态后调用。如果需要更新状态以反映属性的改变,可以在比较this.props和nextProps后,使用this.setState()方法来实现状态的过渡。
注意:React可能会在props值并未改变的时候调用该方法,所以要确保每次处理改变时都要比较当前props和收到的props值。这可能发生在父组件导致该组件重渲染时。
非触发时期:mounting阶段不会调用该方法。只有在部分props属性更新时调用该方法,另外调用this.state()不会触发该方法。
shouldComponentUpdate(nextProps, nextState):该方法用来告诉React,组件输出是否受到当前状态或属性的影响。默认情况下,每次状态改变都会导致重渲染,在大多数情况下,你只需保持该默认行为即可。该方法在收到新的props或state时会被调用,且调用是在重渲染前。该方法默认返回true。但返回false不会影响其子组件在状态改变时的重渲染。
非触发时期:
初次渲染或使用forceUpdate()时不会调用该方法。
注意:就目前而言,如果该方法返回false,以后的componentWillUpdate(),render()和componentDidUpdate()都不会再调用了。但未来可能该方法返回的结果只是作为暗示,而非指令,返回false可能仍会导致重新渲染。
最后,如果找到了导致性能低下的组件,可以使它继承React.PureComponent。该组件用props和state的浅比较实现了shouldComponentUpdate()。也可以自己实现该方法,通过this.props同nextProps比较,this.state同nextState比较,然后返回false以跳过更新。
componentWillUpdate():
该方法在收到新属性和状态渲染前调用。可以用它来做更新前的准备工作。
注意:
该方法不可以调用this.setState()。如果需要更新状态以响应属性的改变,使用componentWillReceiveProps(nextProps)代替。
非触发时期:初次渲染不会调用该方法。shouldComponentUpdate()返回false不会调用该方法。
render():
该方法是mount和update阶段都会使用到的方法,解释同mount阶段的render()。
非触发时期:
shouldComponentUpdate()返回false不会调用该方法。
componentDidUpdate(prevProps, prevState):
更新发生后会立即调用该方法。可用来在组件更新后操作DOM。另外,也可以通过比较当前属性和之前属性来决定是否发送网络请求(如,状态未改变不需要做网络请求)。
非触发时期:
初次渲染不会调用该方法。shouldComponentUpdate()返回false不会调用该方法。
3. Unmounting阶段:
该阶段表示组件将从DOM中移除。该阶段只有一个方法:componentWillUnmount()。
componentWillUnmount():
该方法会在组件被销毁前立即调用。
作用:
可以在方法内实现一些清理工作,如清除定时器,取消网络请求或者是清理其他在componentDidMount()方法内创建的DOM元素。
在组件的整个生命周期中,随着该组件的props或者state发生改变,它的DOM表现也将有相应的改变,一个组件就是一个状态机,对于特定的输入,它总会返回一致的输出。
React为每个组件提供了生命周期钩子函数去响应不同的时刻——创建时、存在期及销毁时。
生命周期方法
React的组件拥有简洁的生命周期API,它仅仅提供你所需要的方法,而不会去最求全面。
实例化:
一个实例出吃被穿件时所调用的生命周期方法与其他哥哥后续实例被创建所调用的方法略有不同。当你首次使用一个组建类时,会看到下面这些方法依次被调用:
getDefaultProps
getInitialState
componentWillMount
render
ComponentDidMOunt
对于该组件类所有后续应用,你将会看到下面的方法依次被调用。注意:gerDefaultProps方法不在列表中。
getInitialState
componentWillMount
render
componentDidMount
存在期:
随着应用状态的改变,以及组件逐渐受到影响,你将会看到下面的方法一次被调用:
componentwillReceiveProps
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
销毁&清理期:
最后,当该组件被使用完成后,componentWillUnmount方法会被调用,目的是给这个实例提供清理自身的机会。
以下是详细说明:
实例化:
当每个新的组件被创建首次渲染时,有一系列的方法可以用来为其做准备工作,这些方法中的每一个斗殴明确的职责,如下所示:
getDefaultProps
对于组件来说,这个方法只会调用一次,对于那些没有父辈组件指定的props属性的新建实例来说,这个方法返回的对象可用与为实例设置默认的props值。
getInitalState:
对于组件的每个实例来说,这个方法调用次数有且仅有一次,这里你将有机会初始化每个实例的state,与getDefaultProps方法不同的是,每次实例创建时该方法都会被调用一次,这个方法中,可以访问到this.props.
componentWillMount:
该方法在完成首次渲染之前被调用,这也是在render方法调用前可以修改组件state的最后一次机会。
render:
在这里你创建一个虚拟DOM,用来表示组件的输出,对于一个组件来说,render是唯一一个必需的方法,并且有特定的规则。render方法选要满足下面几点:
只能通过this.props和this.state访问数据。
可以返回null,false或者任何React组件。
只能出现一个顶级组件(不能返回一组元素)、
必须纯净,有位置不能改变组件状态或者修改DOM输出。
componentDidMount:
在render方法成功调用并且真实的DOM已经被渲染之后,可以在componentDidMount内部通过this.getDOMNode(方法访问到它。
这就是你可以访问原始DOM的生命周期的钩子函数,当你需要测量DOM元素的高度或者使用计时器操作它或者运行jQuery插件时,可以将这些操作挂载到这个方法上:
举例来说,假设需要在一个通过React渲染出的表单元素上使用jQueryUI的Autocomplete插件,则可以这样使用它:
//需要自动补全的字符串列表
var datasource =[...];
var MyComponent=React.crateClass({
render:function(){
rerurn <input .../>
},
componentDidMount:function(){
$(this.gerDOMNode()).autocomplete({
source:datasource
});
}
});
ps:当React运行在服务器端时候,componentdidmount方法不会被调用。
存在期:
此时组件已经渲染好并且用户可以与它进行交互,通常一次鼠标点击、手指点按或者键盘事件触发一个时间处理器,随着用户改变了组件或则和整个应用的state,便会有新的state流入组件树,并且我们将会获得操控它的机会。
componentWillReceiveProps:
任何时刻组件的props都可以通过父辈组件来更改,出现这种情况时,componentWillReceiveProps方法会被调用,你将获得更改props方法及跟他关心state的机会。例如:
componentWillReceiveProps:function(nextProps){
if(nextProps.checked !==undefined){
this.setState({
checked:nextProps.checked
});
}
}
shouleComponentUpdate:
调用shoulecomponentUpadte方法在组件渲染时进行精确优化。如果某个组件或者它的任何子组件不需要渲染成新的props或则和state,则该方法返回false,返回false则是说明React要跳到render方法,一届位于render前后的钩子函数:componentWillUpadate和componentDidUpdate。
该方法非必需的,并且大部分情况没有必要使用它。
componentWillUpdate:
和componentwillMount:方法类似,组建会在收到新的props或者state进行渲染之前调用该方法。
注意:你不可以在该方法中更新huo或者props。而应该借助componentWillreceiveProps方法在运行时更新state。
componentDidUpdate:
和componentDidMount方法类似,该方法给我们更新已经渲染好的DOM机会。
销毁&清理期
当React使用完一个组件,这个组件必须从DOM中卸载随后被销毁。此时仅有的一个狗子函数会做出响应,完成所有的清理和销毁工作。
componentWillUnmount:
最后,随着组件从他的层级结构中移除,这个组件的生命也就走id熬了尽头,该方法会在组件被移除之前调用,让你有机会做一些清理工作。在componentDidMount方法中添加的所有任务都需要在该方法中撤销,比如穿件的定时器或者添加的事件监听器。
反模式:把计算后的值赋值给state:
getInitalState方法中,尝试通过this.props来创建state的做法是一种反模式。React专注于维护数据的单一来源。它的设计使得传递数据的来源更加显而易见,这激素和iReact的一个优势。
比如在组件中吧日期转化成字符串形式,或者渲染之前字符串转换为大写。这些都不是state,只能够在渲染时进行计算。
当组件的state值和它基于的prop不同步,因而无法了解到render函数内部结构时,可以认定为一种反模式。
//反模式:经过计算后值不应该赋给state
getDefaultProps:function(){
return{
date:newDate()
};
},
getInitalState:function(){
return{
day:this.props.date.getDay()
}
},
render:function(){
return <div>Day:{this.state.day}</day>
}
正确的模式应该是渲染时计算这些值,保证了计算后的值永远不会派生出它的props值不同步。
//渲染时计算值是正确的
gerDefaultProps:function(){
return{
date:new Date()
};
}
render:function(){
var day = this.props.date.getDay();
return <div>Day:{day}</div>;
}
然而,如果你的目的并不是同步,而只是简单的初始化state,那么在getInitialState方法中使用props是没问题的,只是一定要明确你的意图,比如prop添加initial前缀。
getDefaultProps:function(){
return{
initialValue:'some-dafault-value'
};
},
getInitialState:function(){
return{
value:this.props.initialValue
};
},
render:function(){
return <div>{this.props.value}</div>
}
总结:
react生命周期提供了进行设计的钩子函数,会伴随着组件整个生命周期。和状态机类似,每个组件都被设计成了能够在整个生命周期中输出稳定、语义化的标签。
组件不会独立存,随着父组件将props推送给他们的子组件,以及那些子组件渲染它们自身的子组件你必须谨慎的考虑数据是如何流经整个应用的。每一个子组件真正需要掌握多少数据,哪个组件来控制应用的状态?这些涉及数据流了。
作者:龙波帝国------------------->打造我的IT帝国
欢迎交流,在下扣号:724711690,请备注:前端技术交流
网友评论