React 生命周期(旧)
先来看图
React组件初始化阶段:
初始化阶段:由ReactDOM.render()触发 — 初次渲染
第一步 constructor()
用来初始化属性状态
1 2 3 4 5 6 7
| constructor(){ super() console.log('constructor--count组件构造器'); this.state = { count: 0, message: '我想要吃的' }
|
第二步 componentWillMount()
组件将要被挂载阶段 (在组件挂载之前触发)
1 2 3
| componentWillMount() { console.log('componentWillMount--count组件将要被挂载'); }
|
第三步 - render()
组件渲染在页面上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| render() { console.log('render--count组件渲染'); const { count } = this.state return ( <div className="count"> <h2>{count}</h2> <button onClick={this.add}>+1</button> <button onClick={this.force}>强制更新,不改变任何数据状态</button> <br /> <div> <h3>我是Count组件</h3> <button onClick={this.changeMessage}>更换子组件信息</button> <CountSon message={this.state.message} /> </div> </div> ) }
|
第四步 componentDidMount()
组件挂载完毕后 (在组件挂载之后,只会执行一次)
1 2 3
| componentDidMount() { console.log('componentDidMount--count组件挂载完毕后'); }
|
这个钩子很常用,我们经常会在这个钩子中做一些初始化的操作,比如:开启定时器,发送网络请求,订阅消息等。
React组件更新阶段:
由组件内部的this.setState()或者父组件的render触发
第一步 componentWillReceiveProps()
组件将要接收参数
组件收到新的属性对象时调用,首次渲染不会触发
一般是父组件向子组件传props时,当子组件更新了props中的数据,就会触发(第一次传过去时,不会触发)
1 2 3 4
| componentWillReceiveProps(props) { console.log('componentWillReceiveProps--Count的子组件', props); }
|
第二步: shouldComponentUpdate()
组件是否应该被更新—-控制组件更新的“阀门”
如果为true就会按照图中所示往下走,如果为false,则将“阀门”关闭,不能往下走了。
1 2 3 4
| shouldComponentUpdate() { console.log('shouldComponentUpdate--count组件是否应该被更新'); return true **}**
|
第三步: componentWillUpdate()
组件将要更新
1 2 3
| componentWillUpdate() { console.log('componentWillUpdate--count组件将要更新'); }
|
第四步:render()
会跟新新的属性对象重新渲染组件
第五步: componentDidUpdate()
组件更新完毕后触发
它接收参数(1.props更新前的,2.state更新前的,3.快照值,(在新的生命周期中会提到))
1 2 3
| componentDidUpdate(preProps, preState, snapShotValue) { console.log('componentDidUpdate--count组件更新完毕后', preProps, preState, snapShotValue); }
|
卸载组件阶段
由ReactDOM.unmountComponentAtNode()触发
componentWillUnmount()
组件将要被卸载时触发,
一般经常在这个钩子中做一些收尾的操作:比如:关闭定时器,取消订阅消息等。
(旧)生命周期代码测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
| import React, { Component } from 'react';
class Count extends Component { constructor() { super() console.log('constructor--count组件构造器'); this.state = { count: 0, message: '我想要吃的' } }
componentWillMount() { console.log('componentWillMount--count组件将要被挂载'); }
componentDidMount() { console.log('componentDidMount--count组件挂载完毕后'); }
shouldComponentUpdate() { console.log('shouldComponentUpdate--count组件是否应该被更新'); return true }
componentWillUpdate() { console.log('componentWillUpdate--count组件将要更新'); }
componentDidUpdate() { console.log('componentDidUpdate--count组件更新完毕后'); }
render() { console.log('render--count组件渲染'); const { count } = this.state return ( <div className="count"> <h2>{count}</h2> <button onClick={this.add}>+1</button> <button onClick={this.force}>强制更新,不改变任何数据状态</button> <br /> <div> <h3>我是Count组件</h3> <button onClick={this.changeMessage}>更换子组件信息</button> <CountSon message={this.state.message} /> </div> </div> ) }
add = () => { const { count } = this.state this.setState({ count: count + 1 }) }
force = () => { this.forceUpdate() }
changeMessage = () => { this.setState({ message: "我想要喝的" }) }
}
class CountSon extends Component {
componentWillReceiveProps(props) { console.log('componentWillReceiveProps--Count的子组件', props); } render() { return ( <div> <h3>我是Count的子组件</h3> <span>我收到父组件的信息:{this.props.message}</span> </div> ) } }
export default Count
|
生命周期(新)
先看图
对比 (旧)生命周期
对比俩张图发现,旧版的废弃了三个”will”,引来了俩个”get”
废弃:
- componentWillMount
- componentWillReceiveProps
- componentWillUpdate
新增:
- getDerivedStateFromProps
- getSnapshotbeforeUpdate
getDerivedStateFromProps
- 是个静态方法,需要在前面加个
static
,否则会报错,如下:
Lifecycle method should be static: getDerivedStateFromProps
- 必须返回属性对象或者null,否则报错,如下:
A valid state object (or null) must be returned. You have returned undefined.
- 可以传参数
参数:新的属性对象(内部数据state),旧的状态对象(外部数据props)
官网也提出了,这个钩子不建议使用, 因为 派生状态会导致代码冗余,并使组件难以维护。在这里不过多学习它了。
getDerivedStateFromProps
在更新之前获取快照
- 有这个钩子必须也要有 componentDidUpdate 这个钩子,需要和它搭配使用
- 必须有返回值,返回值 可以是 值,也可以是
null
1 2 3 4 5 6 7 8 9 10 11 12 13
| getSnapshotBeforeUpdate(){ console.log('getSnapshotBeforeUpdate--count') return 'Wangpf' }
componentDidUpdate(preProps, preState, snapShotValue){ console.log('componentDidUpdate--count组件更新完毕后', preProps, preState, snapShotValue); }
|
getSnapshotBeforeUpdate
获取更新前的快照值,
举个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| import React, { Component } from 'react';
import './News.css'
class News extends Component {
state = { newsArr: [] }
componentDidMount() { setInterval(() => { const { newsArr } = this.state const news = `新闻${newsArr.length + 1}`
this.setState({ newsArr: [news, ...newsArr] }) }, 1000) }
getSnapshotBeforeUpdate() { return this.listHeight.scrollHeight }
componentDidUpdate(preProps, preState, height) { this.listHeight.scrollTop += this.listHeight.scrollHeight - height }
render() { const { newsArr } = this.state return ( <div className="list" ref={c => this.listHeight = c}> {newsArr.map((item, index) => { return <div key={index} className='news'>{item}</div> })} </div> ) } }
export default News
|
上述例子描述的是 , 可以使用 该钩子来获取更新之前的 scroll 高度, 然后到它一直在滚动时,我希望它一直显示某个位置,而不是随之滚动,一直显示顶部。
总结
记住较为重要的几个钩子
- render : 初始化渲染或者更新渲染时调用的
- componentDidMount:组件挂载完毕后调用,用途如: 开启监听,发送ajax请求
- componentWillUnmount: 组件将要卸载时调用, 用途如: 做一些收尾的工作,如清理定时器,取消订阅等。
废弃的钩子
- componentWillMount
- componentWillReceiveProps
- componentWillUpdate
目前版本17.0.1,目前使用的话,会出现警告,需要加上 前缀 UNSAFE_ 才行, 但官方说了在未来18版本之后很可能彻底废弃,因此不建议使用。