组件继承了react Component等相关基类,也就是继承了react的基类,才有render() , 生命周期等方法可以使用,这也说明了函数组件(无状态组件)不能使用这些方法的原因。
组件初始化阶段
类的构造方法(constructor())用来做组件的初始化工作
super(props)用来调用基类的构造方法,也将父组件的props注入给子组件,供子组件读取
组件中props只读不可变,state可变。
import React, { Component } from 'react';
class Test extends Component {
constructor(props) {
super(props);
}
}
constructor(props) {
super(props);
this.comboxHtmlOut = this.props.comboxHtmlOut;
this.pathQuery=this.props.pathQuery
}
state = {
employeeData: {},
submitLoading: false,
selectData:[]
};
constructor()接收两个参数props,context
可以获取到父组件传下来的props,context,如果想在constructor构造函数内部 (注意是内部哦,在组件其他地方是可以直接接收的)使用props或context,则需要传入,并传入super对象。
constructor(props,context) {
super(props,context)
console.log(this.props,this.context) // 在内部可以使用props和context
}
注意:只要组件存在constructor,就必须要写super,否则this指向就会错误
constructor() {
console.log(this) // 报错,this指向错误
}
组件挂载阶段
此阶段分为componentWillMount , render , componentDidMOunt 三个时期
componentWillMount
在组件挂载到DOM前调用,且只会被调用一次,在这边调用this.setState不会引起组件重新渲染,也可以把写在这边的内容提前到constructor()中。
1)组件刚经过constructor初始完数据
2)组件未进入render,未完成渲染
注意:ajax请求不推荐写在这里
原因:
1)虽然有些情况下并不会出错,但是如果ajax请求过来的数据是空,那么会影响页面的渲染,可能看到的就是空白。(个人觉得只要做好相应的异常处理操作即可避免该问题)
2)不利于服务端渲染,在同构的情况下,生命周期会到componentwillmount,这样使用ajax就会出错
render
根据组件的props和state(只要两者重传递和重赋值,无论值是否有变化,都可以引起组件重新render),renter出一个react元素,不负责组件实际渲染工作,之后由React自身根据此元素去渲染出页面DOM。render是纯函数(Pure function:函数的返回结果只依赖于它的参数;函数执行过程里面没有副作用),不能在里面执行this.setState,会有改变组件状态的副作用。
render函数会插入jsx生成的dom结构,react会生成一份虚拟dom树,在每一次组件更新时,在此react会通过其diff算法比较更新前后的新旧DOM树,比较以后,找到最小的有差异的DOM节点,并重新渲染
react16中 render函数允许返回一个数组,单个字符串等,不在只限制为一个顶级DOM节点,可以减少很多不必要的div(当然注意升级你的react版本,将现有项目升到react16并不会出现什么bug,唯一注意的是proTypes类型检测换了名字~)
eg:
render () {
return " "
}
render () {
return [
<div></div>
<div></div>
]
}
componentDidMount
组件挂载到DOM后调用,且只会被调用一次
组件第一次渲染完成,此时dom节点已经生成,可以在这里调用ajax请求,返回数据setState后组件会重新渲染
组件更新阶段
setState引起的state更新或父组件重新render引起的props更新,更新后的state和props相对之前无论是否有变化,都将引起子组件的重新render。
组件更新两类(三种)情况:
1.父组件重新render
1)直接使用,每当父组件重新render导致的重传props,子组件将直接跟着重新渲染,无论props是否有变化。可通过shouldComponentUpdata方法优化。
class Child extends Component {
shouldComponentUpdate(nextProps){ // 应该使用这个方法,否则无论props是否有变化都将会导致组件跟着重新渲染
if(nextProps.someThings === this.props.someThings){
return false
}
}
render() {
return <div>{this.props.someThings}</div>
}
}
2)在componentWillReceiveProps方法中,将props转换成自己的state
class Child extends Component {
constructor(props) {
super(props);
this.state = {
someThings: props.someThings
};
}
componentWillReceiveProps(nextProps) { // 父组件重传props时就会调用这个方法
this.setState({someThings: nextProps.someThings});
}
render() {
return <div>{this.state.someThings}</div>
}
}
在该函数(componentWillReceiveProps)中调用 this.setState() 将不会引起第二次渲染
是因为componentWillReceiveProps中判断props是否变化了,若变化了,this.setState将引起state变化,从而引起render,此时就没必要再做第二次因重传props引起的render了,不然重复做一样的渲染了。
2.组件本身调用setState,无论state有没有变化。可通过shouldComponentUpdate方法优化。
class Child extends Component {
constructor(props) {
super(props);
this.state = {
someThings:1
}
}
shouldComponentUpdate(nextStates){ // 应该使用这个方法,否则无论state是否有变化都将会导致组件重新渲染
if(nextStates.someThings === this.state.someThings){
return false
}
}
handleClick = () => { // 虽然调用了setState ,但state并无变化
const preSomeThings = this.state.someThings
this.setState({
someThings: preSomeThings
})
}
render() {
return <div onClick = {this.handleClick}>{this.state.someThings}</div>
}
}
此阶段分为componentWillReceiveProps,shouldComponentUpdate,componentWillUpdate,render,componentDidUpdate
componentWillReceiveProps(nextProps)
此方法只调用于props引起的组件更新过程中,参数nextProps是父组件传给当前组件的新props。但父组件render方法的调用不能保证重传给当前组件的props是有变化的,所以在此方法中根据nextProps和this.props来查明重传的props是否改变,以及如果改变了要执行啥,比如根据新的props调用this.setState出发当前组件的重新render
componentWillReceiveProps在接受父组件改变后的props需要重新渲染组件时用到的比较多
它接受一个参数nextProps
通过对比nextProps和this.props,将nextProps setState为当前组件的state,从而重新渲染组件
componentWillReceiveProps (nextProps) {
nextProps.openNotice !== this.props.openNotice && this.setState({
openNotice:nextProps.openNotice
},() => {
console.log(this.state.openNotice:nextProps) //将state更新为nextProps,在setState的第二个参数(回调)可以打印出新的state
})
}
<BusinessRight
filterId={this.state.rightFilterId}
/>
******************************************************
componentWillReceiveProps(nextProps) {
if (nextProps.filterId !== this.props.filterId) {
let filterParame={
businessModelId:nextProps.filterId
}
this.setState({filterParame})
this.reloadList(apiUrl.list,nextProps,undefined,filterParame)
}
}
shouldComponentUpdate(nextProps, nextState)
唯一用于控制组件重新渲染的生命周期,由于在react中,setState以后,state发生变化,组件会进入重新渲染的流程,(暂时这么理解,其实setState以后有些情况并不会重新渲染,比如数组引用不变)在这里return false可以阻止组件的更新
因为react父组件的重新渲染会导致其所有子组件的重新渲染,这个时候其实我们是不需要所有子组件都跟着重新渲染的,因此需要在子组件的该生命周期中做判断
此方法通过比较nextProps,nextState及当前组件的this.props,this.state,返回true时当前组件将继续执行更新过程,返回false则当前组件更新停止,以此可用来减少组件的不必要渲染,优化组件性能
对于react初学者,可能涉及这个生命周期的机会比较少,但是如果你的项目开始注重性能优化,随着你对react的喜爱和深入,你就会用到这个生命周期
componentWillUpdate(nextProps, nextState)
此方法在调用render方法前执行,在这边可执行一些组件更新发生前的工作,一般较少用。
shouldComponentUpdate返回true以后,组件进入重新渲染的流程,进入componentWillUpdate,这里同样可以拿到nextProps和nextState
render
render方法在上文讲过,这边只是重新调用。
componentDidUpdate(prevProps, prevState)
此方法在组件更新后被调用,可以操作组件更新的DOM,prevProps和prevState这两个参数指的是组件更新前的props和state
组件更新完毕后,react只会在第一次初始化成功会进入componentDidmount,之后每次重新渲染后都会进入这个生命周期,这里可以拿到prevProps和prevState,即更新前的props和state。
卸载阶段
此阶段只有一个生命周期方法:componentWillUnmount
componentWillUnmount
此方法在组件被卸载前调用,可以在这里执行一些清理工作,比如清楚组件中使用的定时器,清楚componentDidMount中手动创建的DOM元素等,以避免引起内存泄漏。
eg:
componentDidMount() {
this.isMount === true
axios.post().then((res) => {
this.isMount && this.setState({ // 增加条件ismount为true时
aaa:res
})
})
}
componentWillUnmount() {
this.isMount === false
}