React:四、生命周期
1.前言
引出生命周期:需要在页面进入后就主动调函数进行一定操作
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../node_modules/react/umd/react.development.js"></script>
<script src="../node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
// 生命周期回调函数=》生命周期钩子函数=》生命周期函数=》生命周期钩子
class Life extends React.Component{
state = {opacity:1}
change = ()=>{
// 卸载组件
ReactDOM.unmountComponentAtNode(document.getElementById('app'))
}
// 组件挂载完毕
componentDidMount(){
// 定时器不要放在render中,因为render是1+n次更新,n指的每次
// state更新就会执行render,如果定时器放在render中,每次更新state
// 就会增加定时器,会导致构建出无穷多定时器
this.timer = setInterval(()=>{
let {opacity} = this.state;
opacity-=0.1
if(opacity<=0){
opacity=1
}
this.setState({opacity})
},200);
}
//组件将要卸载
componentWillUnmount(){
// 清除定时器
// 如果组件卸载了,但是state还在通过定时器更新,那么有错误,
// 需要注意卸载组件时,也应该删除定时器
clearInterval(this.timer)
}
render(){
return (
<div>
<h2 style={{opacity:this.state.opacity}}>今天是网抑云日子</h2>
<button onClick= {this.change} >点我呀老哥</button>
</div>
)
}
}
ReactDOM.render(<Life/>,document.getElementById('app'))
</script>
</body>
</html>
点击按钮即可卸载组件
2 旧生命周期
组件挂载流程:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../node_modules/react/umd/react.development.js"></script>
<script src="../node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
class OldLife extends React.Component{
constructor(props){
console.log("old:constructor")
super(props)
this.state = {count:0}
}
componentDidMount(){
console.log("old:componentDidMount")
}
componentWillMount(){
console.log("old:componentWillMount")
}
add = ()=>{
const {count} = this.state
this.setState({count:count+1})
}
render(){
console.log("old:render")
const {count} = this.state
return (
<div>
<h2>求和开始:{count}</h2>
<button onClick = {this.add}>点我加1</button>
</div>
)
}
}
ReactDOM.render(<OldLife/>,document.getElementById('app'))
</script>
</body>
</html>
初始页面刷新,钩子函数执行顺序如下:
增加更新的钩子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../node_modules/react/umd/react.development.js"></script>
<script src="../node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
class OldLife extends React.Component{
constructor(props){
console.log("old:constructor")
super(props)
this.state = {count:0}
}
componentDidMount(){
console.log("old:componentDidMount")
}
componentWillMount(){
console.log("old:componentWillMount")
}
add = ()=>{
const {count} = this.state
this.setState({count:count+1})
}
// 控制组件更新的阀门
shouldComponentUpdate(){
console.log("old:shouldComponentUpdate")
// return false将不会更新页面
return true;
}
// 组件更新完毕的钩子
componentDidUpdate(){
console.log("old:componentDidUpdate")
}
render(){
console.log("old:render")
const {count} = this.state
return (
<div>
<h2>求和开始:{count}</h2>
<button onClick = {this.add}>点我加1</button>
</div>
)
}
}
ReactDOM.render(<OldLife/>,document.getElementById('app'))
</script>
</body>
</html>
初始化及点击+1更新后:
2.1 强制更新
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../node_modules/react/umd/react.development.js"></script>
<script src="../node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
class OldLife extends React.Component{
constructor(props){
console.log("old:constructor")
super(props)
this.state = {count:0}
}
componentDidMount(){
console.log("old:componentDidMount")
}
componentWillMount(){
console.log("old:componentWillMount")
}
add = ()=>{
const {count} = this.state
this.setState({count:count+1})
}
// 控制组件更新的阀门
shouldComponentUpdate(){
console.log("old:shouldComponentUpdate")
// return false将不会更新页面
return false;
}
componentWillUpdate(){
console.log("old:componentWillUpdate")
}
// 组件更新完毕的钩子
componentDidUpdate(){
console.log("old:componentDidUpdate")
}
// 强制更新按钮,关闭shouldComponentUpdate阀门也能更新
force = ()=>{
console.log("强制更新")
this.forceUpdate()
}
render(){
console.log("old:render")
const {count} = this.state
return (
<div>
<h2>求和开始:{count}</h2>
<button onClick = {this.add}>点我加1</button>
<button onClick = {this.force}>不更改任何状态中数据,强制更新一下</button>
</div>
)
}
}
ReactDOM.render(<OldLife/>,document.getElementById('app'))
</script>
</body>
</html>
关闭可更新的阀门,执行强制更新:
2.2 componentWillReceiveProps
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../node_modules/react/umd/react.development.js"></script>
<script src="../node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
class A extends React.Component{
state = {name:"初始心情"}
change = ()=>{
this.setState({name:"快乐心情"});
}
render(){
return (
<div>
<div>我是A组件</div>
<button onClick= {this.change}>换心情</button>
<B xin = {this.state.name}/>
</div>
)
}
}
class B extends React.Component{
// 第一次不会调,在父组件A更新state执行render时,B组件在A组件中,也会重新调用B组件的render,这时
// B组件会接收新的父组件传的props,此时会调用componentWillReceiveProps
// 组件将要接收新的props的钩子
componentWillReceiveProps(props){
console.log("我是B的componentWillReceiveProps:",props)
}
// 控制组件更新的阀门
shouldComponentUpdate(){
console.log("old:shouldComponentUpdate")
// return false将不会更新页面
return true;
}
componentWillUpdate(){
console.log("old:componentWillUpdate")
}
// 组件更新完毕的钩子
componentDidUpdate(){
console.log("old:componentDidUpdate")
}
render(){
console.log("B组件--render")
return (
<div>
我是B组件,我的心情是:{this.props.xin}
</div>
)
}
}
ReactDOM.render(<A/>,document.getElementById('app'))
</script>
</body>
</html>
如下:
2.3 旧生命周期总结
/*
1.初始化阶段:
由ReactDOM.render()触发--初次渲染
1.1 constructor 1.2 componentWillMount 1.3 render 1.4 componentDidMount==》
常用,一般在钩子中做初始化操作,例如:开启定时器,发送网络请求,订阅消息
2.更新阶段:
由组件内部this.setState()或父组件render()触发
2.1 shouldComponentUpdate 2.2 componentWillUpdate 2.3 render==》必须使用的钩子
2.4 componentDidUpdate
3.卸载组件,由ReactDOM.unmountComponentAtNode()触发:
3.1 componentWillUnmount==》一般在钩子中做收尾的事,例如:关闭定时器,取消订阅消息等
*/
3 新生命周期
3.1 新旧生命周期对比
在react17版本里,需更新为UNSAFE_componentWillUpdate、UNSAFE_componentWillMount、UNSAFE_componentWillReceiveProps,不会编译报警,componentWillUnmount不需更改:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../node_modules/react/umd/react.development.js"></script>
<script src="../node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
/*
1.初始化阶段:
由ReactDOM.render()触发--初次渲染
1.1 constructor 1.2 componentWillMount 1.3 render 1.4 componentDidMount==》
常用,一般在钩子中做初始化操作,例如:开启定时器,发送网络请求,订阅消息
2.更新阶段:
由组件内部this.setState()或父组件render()触发
2.1 shouldComponentUpdate 2.2 componentWillUpdate 2.3 render==》必须使用的钩子
2.4 componentDidUpdate
3.卸载组件,由ReactDOM.unmountComponentAtNode()触发:
3.1 componentWillUnmount==》一般在钩子中做收尾的事,例如:关闭定时器,取消订阅消息等
*/
class A extends React.Component{
state = {name:"初始心情"}
change = ()=>{
this.setState({name:"快乐心情"});
}
render(){
return (
<div>
<div>我是A组件</div>
<button onClick= {this.change}>换心情</button>
<B xin = {this.state.name}/>
</div>
)
}
}
class B extends React.Component{
// 第一次不会调,在父组件A更新state执行render时,B组件在A组件中,也会重新调用B组件的render,这时
// B组件会接收新的父组件传的props,此时会调用componentWillReceiveProps
// 组件将要接收新的props的钩子
UNSAFE_componentWillReceiveProps(props){
console.log("我是B的componentWillReceiveProps:",props)
}
// 控制组件更新的阀门
shouldComponentUpdate(){
console.log("new:shouldComponentUpdate")
// return false将不会更新页面
return true;
}
UNSAFE_componentWillUpdate(){
console.log("new:componentWillUpdate")
}
// 组件更新完毕的钩子
componentDidUpdate(){
console.log("new:componentDidUpdate")
}
componentDidMount(){
console.log("new:componentDidMount")
}
UNSAFE_componentWillMount(){
console.log("new:componentWillMount")
}
render(){
console.log("B组件--render")
return (
<div>
我是B组件,我的心情是:{this.props.xin}
</div>
)
}
}
ReactDOM.render(<A/>,document.getElementById('app'))
</script>
</body>
</html>
如下:
废弃如上三个钩子,新增了两个钩子:getDerivedStateFromProps、getSnapshotBeforeUpdate
3.2 getDerivedStateFromProps:derived衍生的
组件中有初始的state,且getDerivedStateFromProps中写死,那么一直不会更改(不会按照初始的B组件的state的xin:B初始心情来渲染)
getDerivedStateFromProps接入constructor和shouldComponentUpdate之间:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../node_modules/react/umd/react.development.js"></script>
<script src="../node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
/*
1.初始化阶段:
由ReactDOM.render()触发--初次渲染
1.1 constructor 1.2 componentWillMount 1.3 render 1.4 componentDidMount==》
常用,一般在钩子中做初始化操作,例如:开启定时器,发送网络请求,订阅消息
2.更新阶段:
由组件内部this.setState()或父组件render()触发
2.1 shouldComponentUpdate 2.2 componentWillUpdate 2.3 render==》必须使用的钩子
2.4 componentDidUpdate
3.卸载组件,由ReactDOM.unmountComponentAtNode()触发:
3.1 componentWillUnmount==》一般在钩子中做收尾的事,例如:关闭定时器,取消订阅消息等
*/
class A extends React.Component{
state = {name:"初始心情"}
change = ()=>{
this.setState({name:"快乐心情"});
}
render(){
return (
<div>
<div>我是A组件</div>
<button onClick= {this.change}>换心情</button>
<B xin = {this.state.name}/>
</div>
)
}
}
class B extends React.Component{
// 第一次不会调,在父组件A更新state执行render时,B组件在A组件中,也会重新调用B组件的render,这时
// B组件会接收新的父组件传的props,此时会调用componentWillReceiveProps
// 组件将要接收新的props的钩子
// UNSAFE_componentWillReceiveProps(props){
// console.log("我是B的componentWillReceiveProps:",props)
// }
state = {xin:"B初始心情"}
constructor(props){
super(props)
console.log("new:constructor")
}
// 控制组件更新的阀门
shouldComponentUpdate(){
console.log("new:shouldComponentUpdate")
// return false将不会更新页面
return true;
}
// 组件更新完毕的钩子
componentDidUpdate(){
console.log("new:componentDidUpdate")
}
componentDidMount(){
console.log("new:componentDidMount")
}
static getDerivedStateFromProps(){
console.log("new:getDerivedStateFromProps")
return {xin:"我不改啦"}
}
render(){
console.log("B组件--render")
return (
<div>
我是B组件,我原本心情是:{this.state.xin},我的心情是:{this.props.xin}
</div>
)
}
}
ReactDOM.render(<A/>,document.getElementById('app'))
</script>
</body>
</html>
如下:
现修改如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../node_modules/react/umd/react.development.js"></script>
<script src="../node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
/*
1.初始化阶段:
由ReactDOM.render()触发--初次渲染
1.1 constructor 1.2 componentWillMount 1.3 render 1.4 componentDidMount==》
常用,一般在钩子中做初始化操作,例如:开启定时器,发送网络请求,订阅消息
2.更新阶段:
由组件内部this.setState()或父组件render()触发
2.1 shouldComponentUpdate 2.2 componentWillUpdate 2.3 render==》必须使用的钩子
2.4 componentDidUpdate
3.卸载组件,由ReactDOM.unmountComponentAtNode()触发:
3.1 componentWillUnmount==》一般在钩子中做收尾的事,例如:关闭定时器,取消订阅消息等
*/
class A extends React.Component{
state = {name:"初始心情"}
change = ()=>{
this.setState({name:"快乐心情"});
}
render(){
return (
<div>
<div>我是A组件</div>
<button onClick= {this.change}>换心情</button>
<B xin = {this.state.name}/>
</div>
)
}
}
class B extends React.Component{
// 第一次不会调,在父组件A更新state执行render时,B组件在A组件中,也会重新调用B组件的render,这时
// B组件会接收新的父组件传的props,此时会调用componentWillReceiveProps
// 组件将要接收新的props的钩子
// UNSAFE_componentWillReceiveProps(props){
// console.log("我是B的componentWillReceiveProps:",props)
// }
state = {xin:"B初始心情"}
constructor(props){
super(props)
console.log("new:constructor")
}
// 控制组件更新的阀门
shouldComponentUpdate(){
console.log("new:shouldComponentUpdate")
// return false将不会更新页面
return true;
}
// 组件更新完毕的钩子
componentDidUpdate(){
console.log("new:componentDidUpdate")
}
componentDidMount(){
console.log("new:componentDidMount")
}
static getDerivedStateFromProps(props){
console.log("new:getDerivedStateFromProps")
return props
}
render(){
console.log("B组件--render")
return (
<div>
我是B组件,我原本心情是:{this.state.xin},我的心情是:{this.props.xin}
</div>
)
}
}
ReactDOM.render(<A/>,document.getElementById('app'))
</script>
</body>
</html>
如下,初始渲染B组件就调用了getDerivedStateFromProps:
然后点击换心情:
特殊:当state的值任何时候均取决于props的值时,可以加上getDerivedStateFromProps方法。
可以增加第二个参数state,为B组件初始的state值:
因为getDerivedStateFromProps在挂载和更新之前执行,所以均会拦截初始加载页面和更新页面的全部场景。
3.3 getSnapshotBeforeUpdate
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../node_modules/react/umd/react.development.js"></script>
<script src="../node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
/*
1.初始化阶段:
由ReactDOM.render()触发--初次渲染
1.1 constructor 1.2 componentWillMount 1.3 render 1.4 componentDidMount==》
常用,一般在钩子中做初始化操作,例如:开启定时器,发送网络请求,订阅消息
2.更新阶段:
由组件内部this.setState()或父组件render()触发
2.1 shouldComponentUpdate 2.2 componentWillUpdate 2.3 render==》必须使用的钩子
2.4 componentDidUpdate
3.卸载组件,由ReactDOM.unmountComponentAtNode()触发:
3.1 componentWillUnmount==》一般在钩子中做收尾的事,例如:关闭定时器,取消订阅消息等
*/
class A extends React.Component{
state = {name:"初始心情"}
change = ()=>{
this.setState({name:"快乐心情"});
}
render(){
return (
<div>
<div>我是A组件</div>
<button onClick= {this.change}>换心情</button>
<B xin = {this.state.name}/>
</div>
)
}
}
class B extends React.Component{
// 第一次不会调,在父组件A更新state执行render时,B组件在A组件中,也会重新调用B组件的render,这时
// B组件会接收新的父组件传的props,此时会调用componentWillReceiveProps
// 组件将要接收新的props的钩子
// UNSAFE_componentWillReceiveProps(props){
// console.log("我是B的componentWillReceiveProps:",props)
// }
state = {xin:"B初始心情"}
constructor(props){
super(props)
console.log("new:constructor")
}
// 控制组件更新的阀门
shouldComponentUpdate(){
console.log("new:shouldComponentUpdate")
// return false将不会更新页面
return true;
}
componentDidMount(){
console.log("new:componentDidMount")
}
static getDerivedStateFromProps(props,state){
console.log("new:getDerivedStateFromProps",props,state)
return null
}
// 组件更新完毕的钩子
componentDidUpdate(preProps,preState,snapShot){
console.log("new:componentDidUpdate",preProps,preState,snapShot)
}
// 返回值作为参数传给componentDidUpdate,更新时,先调render,再就是getSnapshotBeforeUpdate,最后是componentDidUpdate
// 更新之前返回快照值
getSnapshotBeforeUpdate(){
console.log("new:getSnapshotBeforeUpdate")
// 快照返回值可以是字符串、对象等等,均可
return null
}
render(){
console.log("B组件--render")
return (
<div>
我是B组件,我原本心情是:{this.state.xin},我的心情是:{this.props.xin}
</div>
)
}
}
ReactDOM.render(<A/>,document.getElementById('app'))
</script>
</body>
</html>
初始:
点击换心情:
3.4 getSnapshotBeforeUpdate使用举栗
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../node_modules/react/umd/react.development.js"></script>
<script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="../node_modules/babel-standalone/babel.min.js"></script>
<style>
.list{
width: 200px;
height: 150px;
background-color: blueviolet;
overflow: auto;
}
.news{
height: 29px;
border-bottom: 1px solid red;
}
</style>
</head>
<body>
<div id ="test">
</div>
<script type="text/babel">
class Xiaoxu extends React.Component{
state = {nArr:[]}
componentDidMount(){
setInterval(() => {
const {nArr} = this.state
const news = "小徐"+(nArr.length+1)
this.setState({nArr:[news,...nArr]})
}, 3000);
}
getSnapshotBeforeUpdate(){
return this.refs.listw.scrollHeight
}
componentDidUpdate(preProps,preState,snapValue){
console.log(snapValue)
this.refs.listw.scrollTop += this.refs.listw.scrollHeight-snapValue
}
render(){
return (
<div className="list" ref = "listw">
{
this.state.nArr.map((x,index)=>{
return <div className="news" key={index}>{x}</div>
})
}
</div>
)
}
}
ReactDOM.render(<Xiaoxu/>,document.getElementById("test"))
</script>
</body>
</html>
虽然一直在滚动,但是鼠标拉动的地方不会被不断增加的顶掉:
3.5 新生命周期总结
/*
1.初始化阶段:
由ReactDOM.render()触发--初次渲染
1.1 constructor 1.2 getDerivedStateFromProps 1.3 render 1.4 componentDidMount==》
常用,一般在钩子中做初始化操作,例如:开启定时器,发送网络请求,订阅消息
2.更新阶段:
由组件内部this.setState()或父组件render()触发
2.1 getDerivedStateFromProps 2.2 shouldComponentUpdate 2.3 render==》必须使用的钩子
2.4 getSnapshotBeforeUpdate 2.5 componentDidUpdate
3.卸载组件,由ReactDOM.unmountComponentAtNode()触发:
3.1 componentWillUnmount==》一般在钩子中做收尾的事,例如:关闭定时器,取消订阅消息等
*/