组件从创建到死亡会经历一些特定的阶段
React组件中包含一系列勾子函数(生命周期回调函数)会在特定的时候调用
我们 在定义组件时,会在特定的生命周期回调函数中做特定的工作
一、旧版本的生命周期
1、初始化阶段
- constructor()
- componentWillMount()
- render()
- componentDidMount()
一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
<script type="text/babel">
class Count extends React.Component{
//构造器
constructor(props){
console.log('Count---constructor');
super(props)
//初始化状态
this.state = {count:0}
}
//组件将要挂载的钩子
componentWillMount(){
console.log('Count---componentWillMount');
}
//组件挂载完毕的钩子
componentDidMount(){
console.log('Count---componentDidMount');
}
render(){
console.log('Count---render');
const {count} = this.state
return(
<div>
<h2>当前求和为:{count}</h2>
</div>
)
}
}
ReactDOM.render(<Count/>,document.getElementById('test'))
</script>
执行的顺序如下:
2、更新阶段
更新阶段有三条路线如下:
(1)调用setState()
更新状态----shouldComponentUpdate----componentWillUpdate----render----componentDidUpdate
shouldComponentUpdate
组件是否应该被更新,控制组件更新的“阀门”。如果在组件中没有写shouldComponentUpdate
勾子,默认返回的是true,如果写了就要返回一个布尔值,如果返回true则能往下走,false不能再往下走。
<script type="text/babel">
class Count extends React.Component{
//构造器
constructor(props){
super(props)
//初始化状态
this.state = {count:0}
}
//加1按钮的回调
add = ()=>{
//获取原状态
const {count} = this.state
//更新状态
this.setState({count:count+1})
}
//控制组件更新的“阀门”
shouldComponentUpdate(){
console.log('Count---shouldComponentUpdate');
return true
}
//组件将要更新的钩子
componentWillUpdate(){
console.log('Count---componentWillUpdate');
}
//组件更新完毕的钩子
componentDidUpdate(){
console.log('Count---componentDidUpdate');
}
render(){
const {count} = this.state
return(
<div>
<h2>当前求和为:{count}</h2>
<button onClick={this.add}>点我+1</button>
</div>
)
}
}
ReactDOM.render(<Count/>,document.getElementById('test'))
</script>
执行的顺序如下:
注意:
每触发一次setState
就会调用一次shouldComponentUpdate
,如果在组件中写了shouldComponentUpdate
没有返回布尔值就会报错。
(2)调用forceUpdate()
强制更新----componentWillUpdate----render----componentDidUpdate
不对状态做出更改,组件也能更新,不受shouldComponentUpdate
阀门的控制
<script type="text/babel">
class Count extends React.Component{
//构造器
constructor(props){
super(props)
//初始化状态
this.state = {count:0}
}
//强制更新按钮的回调
force = ()=>{
this.forceUpdate()
}
//组件将要更新的钩子
componentWillUpdate(){
console.log('Count---componentWillUpdate');
}
//组件更新完毕的钩子
componentDidUpdate(){
console.log('Count---componentDidUpdate');
}
render(){
const {count} = this.state
return(
<div>
<h2>当前求和为:{count}</h2>
<button onClick={this.force}>不更改任何状态中的数据,强制更新一下</button>
</div>
)
}
}
ReactDOM.render(<Count/>,document.getElementById('test'))
</script>
执行的顺序如下:
(3)父组件render----componentWillReceiveProps----shouldComponentUpdate----componentWillUpdate----render----componentDidUpdate
例:有A,B两个组件,A组件维护状态,但是A组件不展示,展示到B组件,形成父子关系
<script type="text/babel">
//父组件A
class A extends React.Component{
//初始化状态
state = {carName:'奔驰'}
changeCar = ()=>{
this.setState({carName:'奥拓'})
}
render(){
return(
<div>
<div>我是A组件</div>
<button onClick={this.changeCar}>换车</button>
<B carName={this.state.carName}/>
</div>
)
}
}
//子组件B
class B extends React.Component{
//组件将要接收新的props的钩子
componentWillReceiveProps(props){
console.log('B---componentWillReceiveProps',props);
}
//控制组件更新的“阀门”
shouldComponentUpdate(){
console.log('B---shouldComponentUpdate');
return true
}
//组件将要更新的钩子
componentWillUpdate(){
console.log('B---componentWillUpdate');
}
//组件更新完毕的钩子
componentDidUpdate(){
console.log('B---componentDidUpdate');
}
render(){
console.log('B---render');
return(
<div>我是B组件,接收到的车是:{this.props.carName}</div>
)
}
}
ReactDOM.render(<A/>,document.getElementById('test'))
</script>
父组件render,子组件响应componentWillReceiveProps
勾子(组件将要接收参数)
注意:
父组件第一次render
不会调用componentWillReceiveProps
,要修改完状态后父组件重新render
才会调用
执行的顺序如下:
3、卸载组件
componentWillUnmount
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
<script type="text/babel">
class Count extends React.Component{
//构造器
constructor(props){
super(props)
//初始化状态
this.state = {count:0}
}
//卸载组件按钮的回调
death = ()=>{
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
//组件将要卸载的钩子
componentWillUnmount(){
console.log('Count---componentWillUnmount');
}
render(){
console.log('Count---render');
const {count} = this.state
return(
<div>
<h2>当前求和为:{count}</h2>
<button onClick={this.death}>卸载组件</button>
</div>
)
}
}
ReactDOM.render(<Count/>,document.getElementById('test'))
</script>
二、新版本的生命周期
新版本的生命周期和旧版本的相比,即将废弃三个勾子:componentWillMount
、componentWillUpdate
、componentWillReceiveProps
同时提出了两个新的勾子:getDerivedStateFromProps
、getSnapshotBeforeUpdate
,剩下的跟旧版本的相同。
注意:
如果在新版本的react中使用即将废弃的旧版本中的三个生命周期勾子,目前能够正常显示但是会报警告,后期不排除不能用直接报错
如果实在要用,需要使用别名UNSAFE_componentWillMount
、UNSAFE_componentWillUpdate
、UNSAFE_componentWillReceiveProps
官网描述 https://react.docschina.org/blog/2018/03/27/update-on-async-rendering.html
1、初始化阶段
- constructor()
- getDerivedStateFromProps()
- render()
- componentDidMount()
<script type="text/babel">
class Count extends React.Component{
//构造器
constructor(props){
super(props)
//初始化状态
this.state = {count:0}
}
//若state的值在任何时候都取决于props,那么可以使用getDerivedStateFromProps
static getDerivedStateFromProps(props,state){
console.log('Count---getDerivedStateFromProps',props,state);
return null
}
//组件挂载完毕的钩子
componentDidMount(){
console.log('Count---componentDidMount');
}
render(){
console.log('Count---render');
const {count} = this.state
return(
<div>
<h2>当前求和为:{count}</h2>
<button onClick={this.death}>卸载组件</button>
</div>
)
}
}
ReactDOM.render(<Count count={199}/>,document.getElementById('test'))
</script>
组件挂载和更新都会触发getDerivedStateFromProps
执行的顺序如下:
注意:
getDerivedStateFromProps()要放在类上而不是实例上,要加上static
,否则会报错如下:
要有返回值,并且值为null或者一个对象,否则会报错如下:
如果返回的是null
,则页面上展示的状态值是constructor中的,并且更改值的操作不会受影响;如果返回的是接到的props
或者其他对象,则页面上展示的是props中的值或者自己写死的值,并且无法触发其他更改值的操作。
官网详细描述:https://zh-hans.reactjs.org/docs/react-component.html#static-getderivedstatefromprops
2、更新阶段
- getDerivedStateFromProps
- shouldComponentUpdate()
- render()
- getSnapshotBeforeUpdate
- componentDidUpdate()
<script type="text/babel">
class Count extends React.Component{
//构造器
constructor(props){
super(props)
//初始化状态
this.state = {count:0}
}
//加1按钮的回调
add = ()=>{
//获取原状态
const {count} = this.state
//更新状态
this.setState({count:count+1})
}
//若state的值在任何时候都取决于props,那么可以使用getDerivedStateFromProps
static getDerivedStateFromProps(props,state){
console.log('Count---getDerivedStateFromProps',props,state);
return null
}
//在更新之前获取快照
getSnapshotBeforeUpdate(){
console.log('Count---getSnapshotBeforeUpdate');
return '888'
}
//控制组件更新的“阀门”
shouldComponentUpdate(){
console.log('Count---shouldComponentUpdate');
return true
}
//组件更新完毕的钩子
componentDidUpdate(preProps,preState,snapshotValue){
console.log('Count---componentDidUpdate',preProps,preState,snapshotValue);
}
render(){
console.log('Count---render');
const {count} = this.state
return(
<div>
<h2>当前求和为:{count}</h2>
<button onClick={this.add}>点我+1</button>
<button onClick={this.death}>卸载组件</button>
</div>
)
}
}
ReactDOM.render(<Count count={199}/>,document.getElementById('test'))
</script>
执行的顺序如下:
如果组件执行完了componentDidUpdate
意味着组件完成更新了,页面上已经出现了更新之后的效果,之前那些东西就获取不到了,利用getSnapshotBeforeUpdate
快照就可以在更新之前获取信息。
官网详细描述:https://zh-hans.reactjs.org/docs/react-component.html#getsnapshotbeforeupdate
例:getSnapShotBeforeUpdate的使用场景
<div id="test"></div>
.list{
width: 200px;
height: 150px;
background-color: skyblue;
overflow: auto;
}
.news{
height: 30px;
}
<script type="text/babel">
class NewsList extends React.Component{
state = {newsArr:[]}
componentDidMount(){
setInterval(() => {
//获取原状态
const {newsArr} = this.state
//模拟一条新闻
const news = '新闻'+ (newsArr.length+1)
//更新状态
this.setState({newsArr:[news,...newsArr]})
}, 1000);
}
getSnapshotBeforeUpdate(){
return this.refs.list.scrollHeight
}
componentDidUpdate(preProps,preState,height){
this.refs.list.scrollTop += this.refs.list.scrollHeight - height
}
render(){
return(
<div className="list" ref="list">
{
this.state.newsArr.map((n,index)=>{
return <div key={index} className="news">{n}</div>
})
}
</div>
)
}
}
ReactDOM.render(<NewsList/>,document.getElementById('test'))
</script>
3、卸载组件
1、componentWillUnmount()
由ReactDOM.unmountComponentAtNode()触发,与旧版本生命周期相同。