React v16.0前的生命周期
其实大部分团队不见得会跟进升到16版本,所以16前的生命周期还是很有必要掌握的,何况16也是基于之前的修改
生命周期可分为四个阶段:
1. 组件初始化(initialization)阶段:
- constructor 方法
- 组件一创建就会调用 es6 class类中的 constructor 方法(最先执行)
- super(props)`用来调用类的构造方法( constructor() ), 也将父组件的props注入给子组件,子组件中props只读不可变,state可变
- 而
constructor()
用来做一些组件的初始化工作,如定义this.state的初始内容
class TodoList extends Component {
// 组件一创建最先执行,早于render
constructor(props) {
super(props);
this.state = {
inputValue: '',
list: []
}
}
}
2. 组件的挂载(Mounting)阶段(已插入真实 DOM)
- componentWillMount 方法
在组件挂载到DOM前调用,且只会被调用一次
,在这边调用this.setState不会引起组件重新渲染,也可以把写在这边的内容提前到constructor()中,所以项目中很少用
- render 方法
根据组件的props和state(两者的重传递和重赋值,无论值是否有变化,都可以引起组件重新render) ,当父组件的 render 函数被运行时, 它的子组件的 render 函数也会被重新运行
return 一个React元素(描述组件,即JSX模板),不负责组件实际渲染工作,之后由React自身根据此元素去渲染出页面DOM。render是纯函数(Pure function:函数的返回结果只依赖于它的参数;函数执行过程里面没有副作用),不能在里面执行this.setState,会有改变组件状态的副作用。
- componentDidMount 方法
组件挂载到DOM后调用,且只会被调用一次
3. 组件的更新(Updating)阶段
- componentWillReceiveProps 方法(nextProps)
(只跟父组件传过来的 props 的变化有关,所以只有能接受 props 参数的子组件才有这个方法)
此方法只调用于props引起的组件更新过程中,参数nextProps是父组件传给当前组件的新props
而且这个组件第一次存在于父组件的是时候不会被执行(就是第一次不会执行,以后如果 nextProps 参数有变化才会被执行
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>
}
}
- shouldComponentUpdate(nextProps, nextState) 方法
此方法通过比较nextProps,nextState及当前组件的this.props,this.state,返回true时当前组件将继续执行更新过程,返回false则当前组件更新停止,以此可用来减少组件的不必要渲染,优化组件性能。
shouldComponentUpdate(nextStates){ // 应该使用这个方法,否则无论state是否有变化都将会导致组件重新渲染
if(nextStates.someThings === this.state.someThings){
return false
}
}
- componentWillUpdate(nextProps, nextState) 方法
此方法在调用render方法前执行,在这边可执行一些组件更新发生前的工作,一般较少用
- render 方法
render方法在上文讲过,这边只是重新调用
- componentDidUpdate(prevProps, prevState)
此方法在组件更新后被调用,可以操作组件更新的DOM,prevProps和prevState这两个参数指的是组件更新前的props和state
4. 卸载阶段
- componentWillUnmount 方法
此方法在组件被卸载前调用,可以在这里执行一些清理工作,比如清楚组件中使用的定时器,清楚componentDidMount中手动创建的DOM元素等,以避免引起内存泄漏。
完整的生命周期在 React 组件中的应用
class TodoList extends Component {
constructor(props) {
console.log('constructor');
super(props);
this.state = {
list: [],
inputValue: ''
}
}
// 组件加载前
componentWillMount() {
console.log('componentWillMount');
}
// nextProps 参数变化后执行 第一次不执行
componentWillReceiveProps(nextProps) { // 只有子组件有
console.log('componentWillReceiveProps');
}
// shouldComponentUpdate 在组件更新前比较参数和数据,自己返回布尔值,优化组件性能
shouldComponentUpdate(nextStates){
console.log('shouldComponentUpdate');
return true;
// if(nextStates.someThings === this.state.someThings){
// return false
// }
}
// componentWillUpdate 在 render 前执行
componentWillUpdate() {
console.log('componentWillUpdate');
}
// 生成描述文件 和 渲染
render() {
console.log('render');
return (
<Fragment>
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
</div>
<div>
<input className='target' value={this.state.inputValue} onChange={this.handleInputChange} />
<button className='red-btn' onClick={this.handleBtnClick}>add</button>
</div>
<ul>
{
this.getTodoItems()
}
</ul>
</div>
</Fragment>
);
}
// 组件加载后
componentDidMount() {
console.log('componentDidMount');
}
// componentDidUpdate 在组件更新后被调用
componentDidUpdate() {
console.log('componentDidUpdate');
}
// componentWillUnmount 在组件被卸载前调用
componentWillUnmount() {
console.log('componentWillUnmount');
}
}
React v16.4 的生命周期
新引入了两个新的生命周期函数
- getDerivedStateFromProps 方法
在组件创建时和更新时的render方法之前调用,它应该返回一个对象来更新状态,或者返回null来不更新任何内容
- getSnapshotBeforeUpdate 方法
在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给 componentDidUpdate()
。
class ScrollingList extends React.Component {
constructor(props) {
super(props);
this.listRef = React.createRef();
}
getSnapshotBeforeUpdate(prevProps, prevState) {
// 我们是否在 list 中添加新的 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>
);
}
}
在上述示例中,重点是从 getSnapshotBeforeUpdate
读取 scrollHeight
属性,因为 “render” 阶段生命周期(如 render
)和 “commit” 阶段生命周期(如 getSnapshotBeforeUpdate
和 componentDidUpdate
)之间可能存在延迟。