什么是生命周期函数?
就是不用本身手动的去调用,框架会在合适的时间自动的调用该函数react
生命周期函数分类:git
生命周期函数执行流程:
算法
constructor
是ES6对类的默认方法,经过 new
命令生成对象实例时自动调用该方法(调用一次)。
而且,该方法是类中必须有的,若是没有显示定义,则会默认添加空的constructor()
方法。当存在constructor
的时候必须手动调用super
方法。
在constructor
中若是要访问this.props
,则须要传入props
,bash
class Counter extends React.component{
constructor(props){
super(props); // 声明constructor时必须调用super方法
console.log(this.props); // 能够正常访问this.props
}
}
复制代码
constructor 经常使用来初始化state框架
constructor(props){
super(props)
this.state = {number:0} // 初始化的状态
}
复制代码
在类组件中也能够经过静态方法设置属性dom
class Counter extends React.component{
static defaultProps = {name:"Fan"} // 默认属性
constructor(props){
super(props); // 声明constructor时必须调用super方法
console.log(this.props); // 能够正常访问this.props
}
}
复制代码
在组件挂载以前调用,且全局只调用一次。异步
若是在这个钩子里调用this.setState
不会引发组件从新渲染,也能够把写在这里的内容写道到constructor()
中,因此不多使用。ide
render是一个React组件必须定义的生命周期函数,用来渲染DOM。函数
并必须 return
一个React元素(描述组件,即UI),不负责组件实际渲染工做,以后由React自身根据此元素去渲染出页面DOM。
render必须是纯函数(Pure function:函数的返回结果只依赖于它的参数;函数执行过程里面没有反作用)性能
不要在render里面修改state,会触发死循环致使栈溢出。
在组件挂载完成后调用,且全局只调用一次。
能够在这里使用refs,获取真实dom元素。
该钩子内也能够发起异步请求,并在异步请求中能够进行setState
。
在讲述此阶段前须要先明确下react组件更新机制。
setState
引发的state
更新或父组件从新render
引发的props
更新,更新后的state
和props
相对以前不管是否有变化,都将引发子组件的从新render
。
props
发生变化以及父组件从新渲染时都会触发该生命周期函数
在该钩子内能够经过参数nextProps
获取变化后的props
参数,
经过this.props
访问以前的props
。该生命周期内能够进行setState
。
组件挂载以后,每次调用setState
后都会调用shouldComponentUpdate
判断是否须要从新渲染组件。就是经过比较nextProps
,nextState
及当前组件的this.props
,this.state
的状态用来判断是否须要从新渲染
默认返回true
,须要从新render
,返回false
则不触发渲染。
通常咱们经过该函数来优化性能
例如:一个React项目须要更新一个小组件时,极可能须要父组件更新本身的状态。而一个父组件的从新更新会形成它其下全部的子组件从新执行render()
方法,造成新的虚拟DOM,再用diff算法对新旧虚拟DOM进行结构和属性的比较,决定组件是否须要从新渲染
无疑这样的操做会形成不少的性能浪费,因此咱们开发者能够根据项目的业务逻辑,在shouldComponentUpdate()
中加入条件判断,从而优化性能
组件即将被更新时触发
shouldComponentUpdate
返回true
或者调用forceUpdate
以后,componentWillUpdate
会被调用。
不能在该钩子中setState
,不然会触发重复循环。
此方法在组件更新后被调用(除了首次render
以后调用componentDidMount
,其它render
结束以后都是调用componentDidUpdate
)
能够操做组件更新的DOM,prevProps
和prevState
这两个参数指的是组件更新前的props
和state
该钩子内
setState
有可能会触发重复渲染,须要自行判断,不然会进入死循环。
此方法在组件被卸载前调用
能够在这里执行一些清理工做,好比清除组件中使用的定时器、取消Redux的订阅事件、清除componentDidMount
中手动建立的DOM元素等等,以免引发内存泄漏。
import React, { Component } from 'react';
class Father extends React.Component{
static defaultProps = {name:"Fan"} // 默认属性
constructor(props){
super(props)
this.state = {number:0}
}
UNSAFE_componentWillMount(){
console.log("Father: componentWillMount") // 1
}
shouldComponentUpdate(nextProps,nextState){
console.log("Father: shouldComponentUpdate") //
// if(nextState.number%2 === 0){
// return true
// }else{
// return false;
// }
return true;
}
componentWillUpdate(){
console.log("Father: componentWillUpdate") //
}
componentDidUpdate(){
console.log("Father: componentDidUpdate")
}
handleClick = ()=>{
this.setState({number:this.state.number+1})
}
render(){
console.log("Father: render") // 2
return(
<div>
<h1>父组件</h1>
<p>{this.state.number}</p>
<button onClick={this.handleClick}>+</button>
<hr/>
{this.state.number%2==0 ? <Son number={this.state.number}></Son> : null}
</div>
)
}
componentDidMount(){
console.log("Father: componentDidMount") // 3
}
}
class Son extends React.Component{
UNSAFE_componentWillMount(){
console.log("Son: componentWillMount") // 1
}
componentDidMount(){
console.log("Son: componentDidMount") // 3
}
componentWillReceiveProps(){
console.log("Son: componentWillReceiveProps")
}
shouldComponentUpdate(){
console.log("Son: shouldComponentUpdate")
// return false;
return true;
}
componentWillUpdate(){
console.log("Son: componentWillUpdate") //
}
componentDidUpdate(){
console.log("Son: componentDidUpdate")
}
render(){
console.log("Son: render")
return(
<div>
<h1>子组件</h1>
<p>{this.props.number}</p>
</div>
)
}
componentWillUnmount(){
console.log("Son: componentWillUnmount")
}
}
export default Father;
复制代码
运行结果(须要下去本身慢慢分析):
移除的生命周期函数:
虽然废弃了这三个生命周期方法,可是为了向下兼容,将会作渐进式调整。
在16.3 版本并未删除这三个生命周期,同时还为它们新增以UNSAFE_
前缀为别名的三个函数UNSAFE_componentWillMount()
、UNSAFE_componentWillReceiveProps()
、UNSAFE_componentWillUpdate()
。
在 16.4 版本给出警告将会弃用componentWillMount()
、componentWillReceiveProps()
、componentWillUpdate()
三个函数
而后在 17.0 版本将会删除componentWillMount()
、componentWillReceiveProps()
、componentWillUpdate()
这三个函数,会保留使用UNSAFE_componentWillMount()
、UNSAFE_componentWillReceiveProps()
、UNSAFE_componentWillUpdate()
新增的声明周期函数:
从上面的生命周期的图中能够看出,被废弃的三个函数都是在render
以前,由于fiber的出现,极可能由于高优先级任务的出现而打断现有任务致使它们会被执行屡次,这确定不是你想要的结果。
另外的一个缘由则是,React想约束使用者,好的框架可以让人不得已写出容易维护和扩展的代码。
有篇文章感受写的很好新老生命周期对比
在每次渲染以前都会调用,无论形成从新渲染的缘由是什么,无论初始挂载仍是后面的更新都会调用,这一点和UNSAFE_componentWillReceiveProps
不一样(只有当父组件形成从新渲染时才调用)
该方法传入的两个参数:nextProps
表示父组件传入的值,prevState
表示组件自身的state
使用该方法,须要在该方法中返回一个对象或null:若是返回的是对象,则会更新 state,若是返回的是null,则表示不更新。
使用该方法的时候须要初始化state,不然在控制台中会出现警告信息,不能在该方法内部,调用
this.state
这个方法在 render()
以后,componentDidUpdate()
以前调用。
该方法传入的两个参数:prevProps
表示更新前的 props,prevState
表示更新前的 state
返回值称为一个快照(snapshot),若是不须要 snapshot,则必须显示的返回 null
—— 由于返回值将做为 componentDidUpdate()
的第三个参数使用。因此这个函数必需要配合 componentDidUpdate()
一块儿使用。
这个函数的做用是在真实 DOM 更新(
componentDidUpdate
)前,获取一些须要的信息(相似快照功能),而后做为参数传给componentDidUpdate
。例如:在getSnapShotBeforeUpdate
中获取滚动位置,而后做为参数传给componentDidUpdate
,就能够直接在渲染真实的 DOM 时就滚动到须要的位置。
在上面没说,这里说一下
任何子组件在渲染期间,生命周期方法中或者构造函数 constructor
发生错误时调用。
该方法传入的两个参数:err
表示抛出的错误,info
表示带有componentStack key
的对象,其中包含有关组件引起错误的栈信息。
错误边界不会捕获下面的错误:
import React, { Component } from 'react';
class Father extends Component{
constructor(props){
super(props)
this.state = {number:0}
}
handleClick = ()=>{
this.setState({number:this.state.number+1})
}
render(){
return(
<div>
<h1>父组件</h1>
<p>{this.state.number}</p>
<button onClick={this.handleClick}>+</button>
<hr/>
<Son number={this.state.number}></Son>
</div>
)
}
}
class Son extends React.Component{
state = {
number:0
}
// nextProps,prevState
// nextProps 就是父给子传递的过来的新的数据
// prevState 是子的上一次的状态
//每次更新都会触发
static getDerivedStateFromProps(nextProps,prevState){
// nextProps 表示父向下传递的新的值 1 2 3 4 5
// prevState 0 0 0
console.log(nextProps,prevState) // {number: 0} {number: 0}
let {number} = nextProps;
// console.log(number)
// if(number%2 === 0){
// return { number:number+10 }
// }else{
// return { number:number+100 }
// }
// prevState 表示是子组件的上一次状态
if(number%2 === 0){
console.log(prevState.number)
return { number:prevState.number + number+10 }
}else{
console.log(prevState.number)
return { number:prevState.number + number+100 }
}
}
handleChange = ()=>{
this.setState({
number:this.state.number+1000
})
}
render(){
return(
<div>
<h1>子组件</h1>
<p>{this.state.number}</p>
<button onClick={this.handleChange}>改变状态</button>
</div>
)
}
}
export default Father;
复制代码
运行效果:
import React, { Component } from 'react';
class News extends Component {
constructor(props) {
super(props)
this.scrollRef = React.createRef();
}
state = {
// news:["news6","news5","new4","news3","news2","new1"]
news: []
}
componentDidMount() {
this.timer = setInterval(() => {
this.setState({
news: [`${this.state.news.length}`, ...this.state.news]
})
}, 1000)
}
componentWillUnmount() {
clearInterval(this.timer)
}
// 获取更新以前dom的快照
getSnapshotBeforeUpdate() {
// console.log(this.scrollRef.current.scrollHeight)
// 在getSnapshotBeforeUpdate中返回了一个值,这个值会给componedDidUpdate的最后一个参数
return this.scrollRef.current.scrollHeight;
}
componentDidUpdate(prevProps, prevState, lastScrollHeight) {
console.log(lastScrollHeight)
console.log(this.scrollRef.current.scrollTop) // 0
let scrollTop = this.scrollRef.current.scrollTop;
this.scrollRef.current.scrollTop = scrollTop + (this.scrollRef.current.scrollHeight - lastScrollHeight)
}
render() {
let styles = {
height: "100px",
width: "200px",
border: "1px solid red",
overflow: "auto"
}
return (
<ul style={styles} ref={this.scrollRef}>
{
this.state.news.map((item, index) => <li key={index}>{item}</li>)
}
</ul>
)
}
}
export default News;
复制代码
运行效果: