- 书籍《深入浅出React和Redux》
组件生命周期
React 组件生命周期可能会经历如下三个过程:
- 装载过程(Mount)- 组件第一次在DOM树中渲染的过程
- 更新过程(Update)- 当组件被重新渲染的过程
- 卸载过程(Unmount)- 组件从DOM中删除的过程
装载过程
- constructor()
getInitialState()getDefaultProps()- componentWillMount()
- render()
- componentDidMount()
constructor()
constructor() 是ES6中
每个类的构造函数,要创建一个组件类的实例,当然会调用对应的构造函数。
React中分为函数组件(Functional Component )和类组件(Class Component),划分依据是根据组件的定义方式。函数组件使用函数定义组件,类组件使用ES6 class定义组件。
函数组件或者说无状态组件(不需要维护state的组件)不需要定义构造函数。
一个React组件需要够着函数,目的如下:
- 初始化state,因为组件生命周期中任何函数都可能要访问state,那么整个生命周期中第一个被调用的构造函数自然是初始化state最理想的地方;
- 绑定成员函数的 this 环境。
在ES6语法下,类的每个成员函数在执行时的this并不是和类的实例自动绑定的。而在构造函数中,this就是当前组件的实例,所以,为了方便将来的调用,往往在构造函数中将这个实例的特定函数绑定this为当前实例。
getInitialState() and getDefaultProps()
getInitialState()getDefaultProps()
这两个函数只有用React.createClass() 方法创造的组件类才会发生作用。在ES6用class语法糖
创建的组件类这个函数根本不会产生作用。
在ES6中,对应的可以使用类属性来设置默认的props,ClassName.defaultProps = {xx:xx};
可以直接在构造函数中给state设置默认值。
render()
React组件的父组件React.Component类对除了render()方法之外的生命周期函数都有默认实现,所以render()函数必须要自己实现。
render函数并不做实际的渲染动作,它只是返回一个JSX描述的结构,最终由React来操作渲染过程。
render()函数应该是一个纯函数,完全根据this.state 和 this.props来决定返回的结果,而且不产生任何副作用。在render()函数中调用 this.setState() 毫无疑问是错误的,因为一个纯函数不应该引起状态的改变。
componentWillMount() 和 componentDidMount()
这两个函数在render前后进行调用。
componentWillMount()发生在“将要装载”的时候,这个时候还没有任何渲染效果,即使调用 this.setState修改状态也不会引发重新绘制,一切都迟了。换句话说,所有可以在这个componentWillMount()中做的事情,都可以提前到constructor中去做。可以认为这个函数只是为了和componentDidmount对称
componentDidMount()作用大,在render函数被调用完之后,componentDidMount()并不是会被立刻调用,componentDidMount() 被调用的时候,render函数返回的东西已经引发了渲染,组件已经被装载到DOM树上。
父组件ControlPanel
class ControlPanel extends Component {
render() {
return (
<div style={style}>
<Counter caption="First"/>
<Counter caption="Second" initValue={10} />
<Counter caption="Third" initValue={20} />
<button onClick={ () => this.forceUpdate() }>
Click me to re-render!
</button>
</div>
);
}
}
子组件Counter
class Counter extends Component {
constructor(props) {
super(props);
this.onClickIncrementButton = this.onClickIncrementButton.bind(this);
this.onClickDecrementButton = this.onClickDecrementButton.bind(this);
this.state = {
count: props.initValue
}
}
onClickIncrementButton() {
this.setState({count: this.state.count + 1});
}
onClickDecrementButton() {
this.setState({count: this.state.count - 1});
}
render() {
const {caption} = this.props;
return (
<div>
<button style={buttonStyle} onClick={this.onClickIncrementButton}>+</button>
<button style={buttonStyle} onClick={this.onClickDecrementButton}>-</button>
<span>{caption} count: {this.state.count}</span>
</div>
);
}
}
componentWillMount() 都是紧贴着自己组件的render函数之前被调用,但是componentDidMount() 可不是紧跟着render函数之后,当父组件里面的所有子组件的render函数都被调用之后,子组件的componentDidMount() 才按照子组件的顺序连在一起被调用。
render函数本身并不往DOM树上渲染或者装载内容,它只是返回一个JSX表示的对象,然后由react库根据返回对象决定如何渲染。而React库肯定要把所有组件返回的结果综合起来,才能知道如何产生对应的DOM修改
。
更新过程
当props或者state被修改时,会引发组件的更新过程。更新过程会依次调用下面的生命周期,但并不是所有的更新过程都会执行全部函数。
- componentWillReceiveProps()
- shouldComponentUpdate()
- componentWillUpdate()
- render()
- componentDidUpdate()
componentWillReceiveProps(nextProps)
对于这个函数,有些地方认为这个函数只有当组件的props发生改变的时候才会被调用,这种说法是错误的。
实际上,只要是父组件的render函数被调用,在render函数里面被渲染的子组件就会经历更新过程,不管父组件传给子组件的props有没有改变,都会触发子组件的componentWillReceiveProps()函数。
但是,注意一点,通过this.setState() 方法触发的更新过程不会调用这个函数,这是因为这个函数适合根据新的props值(也就是nextProps 参数)来计算出是不是要更新内部状态state。更新组件内部状态的方法就是this.setState,如果this.setState()的调用导致componentWillReceiveProps()再次被调用,那就是一个死循环。
shouldComponentUpdate(nextProps,nextState)
除了render函数,shouldComponentUpdate()可能是React组件生命周期中最重要的一个函数了。这两个函数也是React生命周期中两个要求有返回结果的函数。
- render函数决定了该渲染什么,返回一个JSX描述的结构,
- shouldComponentUpdate 函数决定了一个组件什么时候
不需要
渲染,返回Boolean值。React默认的实现方式就是简单的返回true
在更新过程中,React库首先调用shouldComponentUpdate函数,如果返回结果是true,那么就继续更新过程,接下来调用render函数,如果是false,那么就会立刻停止更新,不会引发后续的渲染。所以,使用shouldComponentUpdate函数可以提高性能。
在Counter组件里面加上重写的shouldComponentUpdate函数,如果nextProps和nextState没有变化,那么将不进行渲染。
shouldComponentUpdate(nextProps, nextState) {
return (nextProps.caption !== this.props.caption) ||
(nextState.count !== this.state.count);
}
点击click re-render按钮后,因为从父组件传递的props没有改变,而Counter子组件内部state也没有改变,所以不进行渲染,没有进行render方法。
componentWillUpdate 和 componentDidUpdate
卸载过程
componentWillUnmount
卸载过程只有这一个函数,适合做一些清理工作。和装载、更新过程不一样,这个函数没有配对的Did函数,因为卸载完了就没有卸载完了再做的事情了。