1.引出react的生命周期
1.1 一个需求
- h2标签2秒内透明度从1到0
- 如果透明度为0则立马初始为1
- 一个按钮,点击按钮组件被卸载
1.2 组件的卸载(unmount)和装载(mount)
- 装载: 组件被react加载都页面上展示
- 卸载: 组件从页面上清除
- 查看组件通过react 的 dev-tools查看工具
1.3 写出静态页面
- 先写出静态页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>案例引出组件的生命周期</title>
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
class Life extends React.Component{
state={opacity:1}
render(){
return (
//只有一个根节点元素
<div>
<h2>React学不会怎么办</h2>
<button onClick={this.death}>不活了</button>
</div>
);
}
}
ReactDOM.render(<Life/>,document.getElementById("root"))
</script>
</body>
</html>
- 输出结果如下:
1.4 写出定时器
//设置定时器
setInterval(()=>{
//获取原来的状态
let {opacity} = this.state;
//每次减小0.1
opacity -= 0.1;
if(opacity<=0){
opacity = 1;
}
//设置新的透明度
//this.setState({opacity:opacity});
}, 200)
1.5 定时器放在组件的render()方法中(错误写法)
- 如果定时器放到组件的render()方法中,会导致整个render()方法进入一个无限递归的状态;
- 分析: 定时器放入render,render方法是在整个流程中执行 1+n次,每次更新状态就会执行一次render(),而且如果要改变透明度肯定要在类组件内定义state状态属性,每次属性的值改变就会调用一次组件的render()方法,所以是无限次的在调用定时器;
- 而我们的定时器只是希望只在组件装载完成后,执行一次就行;
- 所以要借用react的自身一个方法 componentDidMount(){} --组件已经装载的方法,
- componentDidMount(){}方法和render方法一样直接写,所以修改后代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>案例引出组件的生命周期</title>
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
class Life extends React.Component{
state={opacity:1}
death = ()=>{
//卸载组件 unmount
ReactDOM.unmountComponentAtNode(document.getElementById("root"));
}
componentDidMount(){
console.log("componentDidMount....");
//设置定时器
setInterval(()=>{
//获取原来的状态
let {opacity} = this.state;
//每次减小0.1
opacity -= 0.1;
if(opacity<=0){
opacity = 1;
}
//设置新的透明度
this.setState({opacity:opacity});
}, 200)
}
render(){
console.log("render....")
return (
//只有一个根节点元素
<div>
<h2 style={{opacity:this.state.opacity}}>React学不会怎么办</h2>
<button onClick={this.death}>不活了</button>
</div>
);
}
}
ReactDOM.render(<Life/>,document.getElementById("root"))
</script>
</body>
</html>
- 那么render和componentDidMount哪个先执行?
- 肯定是render先执行;
- 因为componentDidMount是组件装载完成才会执行的方法;
1.6 点击按钮,卸载组件
- 把 death方法赋给 button按钮
- 代码如下:
class Life extends React.Component{
state={opacity:1}
death = ()=>{
//卸载组件 unmount
ReactDOM.unmountComponentAtNode(document.getElementById("root"));
}
//组件完成装载
componentDidMount(){
console.log("componentDidMount....");
//设置定时器
setInterval(()=>{
//获取原来的状态
let {opacity} = this.state;
//每次减小0.1
opacity -= 0.1;
if(opacity<=0){
opacity = 1;
}
//设置新的透明度
this.setState({opacity:opacity});
}, 200)
}
//组件渲染
render(){
console.log("render....")
return (
//只有一个根节点元素
<div>
<h2 style={{opacity:this.state.opacity}}>React学不会怎么办</h2>
<button onClick={this.death}>不活了</button>
</div>
);
}
}
ReactDOM.render(<Life/>,document.getElementById("root"))
- 点击按钮,发现组件的确被卸载了
- 如下图:
- 但是看控制台:
- 警告内容:Can’t perform a React state update on an unmounted component.
- 翻译如下: 无法在卸载的组件上执行React状态更新。
- 意思就是说组件卸载了,但是定时器还在执行组件的状态更新,所以才会报这个错误警告;
- 所以我们 希望能够在组件被卸载之前,停止掉定时器;
- 我们看 death方法,它的里面this是组件的实例对象;
- 而componentDidMount方法中 this也是组件的实例对象;
- 所以在componentDidMount方法中我们把定时器对象放到this中去,
- 然后在death方法中通过this去控制定时器停止
<script type="text/babel">
class Life extends React.Component{
state={opacity:1}
death = ()=>{
//清除定时器
clearInterval(this.timer);
//卸载组件 unmount
ReactDOM.unmountComponentAtNode(document.getElementById("root"));
}
//组件完成装载
componentDidMount(){
console.log("componentDidMount....");
//设置定时器,并把定时器对象放入this中去
this.timer = setInterval(()=>{
//获取原来的状态
let {opacity} = this.state;
//每次减小0.1
opacity -= 0.1;
if(opacity<=0){
opacity = 1;
}
//设置新的透明度
this.setState({opacity:opacity});
}, 200)
}
//组件渲染
render(){
console.log("render....")
return (
//只有一个根节点元素
<div>
<h2 style={{opacity:this.state.opacity}}>React学不会怎么办</h2>
<button onClick={this.death}>不活了</button>
</div>
);
}
}
ReactDOM.render(<Life/>,document.getElementById("root"))
</script>
```
-
输出结果如下: 没有报错
-
但是这是通过按钮点击事件触发,那么我希望在没有触发的情况下,单纯的只是在组件被卸载的时候,做一些事情,该如何做?
-
也就是说希望能在组件卸载之前我们能做一些自己的操作;
1.7 componentWillUnmount组件被卸载前执行的函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>案例引出组件的生命周期</title>
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
class Life extends React.Component{
state={opacity:1}
death = ()=>{
console.log("button will click....");
//卸载组件 unmount
ReactDOM.unmountComponentAtNode(document.getElementById("root"));
console.log("ReactDOM.unmountComponentAtNode....");
console.log("button did click....");
}
//组件完成装载
componentDidMount(){
console.log("componentDidMount....");
//设置定时器,并把定时器对象放入this中去
this.timer = setInterval(()=>{
//获取原来的状态
let {opacity} = this.state;
//每次减小0.1
opacity -= 0.1;
if(opacity<=0){
opacity = 1;
}
//设置新的透明度
this.setState({opacity:opacity});
}, 200)
}
//组件即将被卸载的时候
componentWillUnmount(){
console.log("componentWillUnmount....");
//清除定时器
clearInterval(this.timer);
}
//组件渲染
render(){
console.log("render....")
return (
//只有一个根节点元素
<div>
<h2 style={{opacity:this.state.opacity}}>React学不会怎么办</h2>
<button onClick={this.death}>不活了</button>
</div>
);
}
}
ReactDOM.render(<Life/>,document.getElementById("root"))
</script>
</body>
</html>
- 输出结果如下:
- 隐隐约约能够感觉到react的生命周期,
- 在某些时间点,react总会做一些事情
- 这些特殊的时间点,我们也能够做一些自己的事情;
1.8 生命周期图示:
1.9 生命周期函数叫法
- 生命周期函数;
- 生命周期钩子函数;
- 生命周期函数;
- 生命周期钩子;
2. 组件的生命周期(旧)
2.1 组件的生命周期图(旧)
2.2 组件的生命周期使用
2.2.1 挂载时的生命周期
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>demo</title>
</head>
<body>
<!--1.准备好容器-->
<div id="root"></div>
<!--2.引入js文件-->
<script type="application/javascript" src="../js/react.development.js"></script>
<script type="application/javascript" src="../js/react-dom.development.js"></script>
<script type="application/javascript" src="../js/babel.min.js"></script>
<script type="application/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
//3.1 创建组件
class Count extends React.Component{
//组件构造器
constructor(props) {
super(props);
console.log("Count组件生命周期1 组件构造器-constructor");
//初始化state
this.state = {count:0};
}
//按钮点击回调 add函数
add = ()=>{
//获取原状态
let count = this.state.count;
//求和并更新state的值
this.setState({count:count+1});
}
//卸载组件
death = ()=>{
ReactDOM.unmountComponentAtNode(document.getElementById("root"))
}
//组件将要挂载
componentWillMount(){
console.log("Count组件生命周期2 组件将要挂载-componentWillMount");
}
//组件已经挂载
componentDidMount(){
console.log("Count组件生命周期4 组件已经挂载-componentDidMount");
}
//渲染虚拟dom
render(){
console.log("Count组件生命周期3 渲染虚拟dom-render");
return (
<div>
<h2>点击后总和={this.state.count}</h2>
<button onClick={this.add}>点击+1</button>
<button onClick={this.death}>卸载组件</button>
</div>
);
}
//组件将要被卸载
componentWillUnmount(){
console.log("Count组件生命周期end 组件将要被卸载-componentWillUnmount");
}
}
//3.2 渲染组件到页面
ReactDOM.render(<Count/>, document.getElementById("root"))
</script>
</body>
</html>
- 看下输出结果:
- 初始化页面效果
- 初始化页面效果
- 点击+1 按钮点击
- 点击 卸载组件 按钮
- 可以看到Count组件的挂载时的生命周期如上图
2.2.2 父组件的render
- 这里面又分三条线路,如下图:
(1) 线路1 setState更新组件状态
(1.1)shouldComponentUpdat(){}组件是否render
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>demo</title>
</head>
<body>
<!--1.准备好容器-->
<div id="root"></div>
<!--2.引入js文件-->
<script type="application/javascript" src="../js/react.development.js"></script>
<script type="application/javascript" src="../js/react-dom.development.js"></script>
<script type="application/javascript" src="../js/babel.min.js"></script>
<script type="application/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
//3.1 创建组件
class Count extends React.Component{
//组件构造器
constructor(props) {
super(props);
console.log("Count组件生命周期1 组件构造器-constructor");
//初始化state
this.state = {count:0};
}
//按钮点击回调 add函数
add = ()=>{
//获取原状态
let count = this.state.count;
//求和并更新state的值
this.setState({count:count+1});
}
//卸载组件
death = ()=>{
ReactDOM.unmountComponentAtNode(document.getElementById("root"))
}
//组件将要挂载
componentWillMount(){
console.log("Count组件生命周期2 组件将要挂载-componentWillMount");
}
//组件已经挂载
componentDidMount(){
console.log("Count组件生命周期4 组件已经挂载-componentDidMount");
}
//渲染虚拟dom
render(){
console.log("Count组件生命周期3 渲染虚拟dom-render");
return (
<div>
<h2>点击后总和={this.state.count}</h2>
<button onClick={this.add}>点击+1</button>
<button onClick={this.death}>卸载组件</button>
</div>
);
}
//组件将要被卸载
componentWillUnmount(){
console.log("Count组件生命周期end 组件将要被卸载-componentWillUnmount");
}
//组件是否要更新,有返回值
//如果此方法不写,则react默认此方法返回值为true
//该函数相当于一个开关的效果 true-state更新继续流程,false-state更新,不进行下一步,最终组件不会更新
shouldComponentUpdate(){
console.log("Count组件生命周期 组件是否要更新-shouldComponentUpdate");
return true;
}
}
//3.2 渲染组件到页面
ReactDOM.render(<Count/>, document.getElementById("root"))
</script>
</body>
</html>
- 返回true
- 返回false
(1.2)componentWillUpdate(){}组件即将render
(1.3)componentDidUpdate(){}组件已经render
- 举例如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>demo</title>
</head>
<body>
<!--1.准备好容器-->
<div id="root"></div>
<!--2.引入js文件-->
<script type="application/javascript" src="../js/react.development.js"></script>
<script type="application/javascript" src="../js/react-dom.development.js"></script>
<script type="application/javascript" src="../js/babel.min.js"></script>
<script type="application/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
//3.1 创建组件
class Count extends React.Component{
//组件构造器
constructor(props) {
super(props);
console.log("Count组件生命周期1 组件构造器-constructor");
//初始化state
this.state = {count:0};
}
//按钮点击回调 add函数
add = ()=>{
//获取原状态
let count = this.state.count;
//求和并更新state的值
this.setState({count:count+1});
}
//卸载组件
death = ()=>{
ReactDOM.unmountComponentAtNode(document.getElementById("root"))
}
//组件将要挂载
componentWillMount(){
console.log("Count组件生命周期2 组件将要挂载-componentWillMount");
}
//组件已经挂载
componentDidMount(){
console.log("Count组件生命周期4 组件已经挂载-componentDidMount");
}
//渲染虚拟dom
render(){
console.log("Count组件生命周期3 渲染虚拟dom-render");
return (
<div>
<h2>点击后总和={this.state.count}</h2>
<button onClick={this.add}>点击+1</button>
<button onClick={this.death}>卸载组件</button>
</div>
);
}
//组件将要被卸载
componentWillUnmount(){
console.log("Count组件生命周期end 组件将要被卸载-componentWillUnmount");
}
//组件是否要更新,有返回值
//如果此方法不写,则react默认此方法返回值为true
//该函数相当于一个开关的效果 true-state更新继续流程,false-state更新,不进行下一步,最终组件不会更新
shouldComponentUpdate(){
console.log("Count组件生命周期 组件是否要render-shouldComponentUpdate");
return true;
}
//组件即将更新
componentWillUpdate(){
console.log("Count组件生命周期 组件即将render-componentWillUpdate");
}
//组件已经更新
componentDidUpdate(){
console.log("Count组件生命周期 组件已经render-componentDidUpdate");
}
}
//3.2 渲染组件到页面
ReactDOM.render(<Count/>, document.getElementById("root"))
</script>
</body>
</html>
- 输出结果
- 点击 卸载组件 按钮
(2)线路2 forceUpdate 强制更新
- 不想更新状态state,但想更新组件,执行render
- 绕过shouldComponentUpdate 开关,直接更新
- 举例如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>demo</title>
</head>
<body>
<!--1.准备好容器-->
<div id="root"></div>
<!--2.引入js文件-->
<script type="application/javascript" src="../js/react.development.js"></script>
<script type="application/javascript" src="../js/react-dom.development.js"></script>
<script type="application/javascript" src="../js/babel.min.js"></script>
<script type="application/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
//3.1 创建组件
class Count extends React.Component{
//组件构造器
constructor(props) {
super(props);
console.log("Count组件生命周期1 组件构造器-constructor");
//初始化state
this.state = {count:0};
}
//按钮点击回调 add函数
add = ()=>{
//获取原状态
let count = this.state.count;
//求和并更新state的值
this.setState({count:count+1});
console.log(this.state.count);
}
//卸载组件
death = ()=>{
ReactDOM.unmountComponentAtNode(document.getElementById("root"))
}
//强制更新
force = ()=>{
console.log("Count组件生命周期 组件强制更新-forceUpdate");
this.forceUpdate()
}
//组件将要挂载
componentWillMount(){
console.log("Count组件生命周期2 组件将要挂载-componentWillMount");
}
//组件已经挂载
componentDidMount(){
console.log("Count组件生命周期4 组件已经挂载-componentDidMount");
}
//渲染虚拟dom
render(){
console.log("Count组件生命周期3 渲染虚拟dom-render");
return (
<div>
<h2>点击后总和={this.state.count}</h2>
<button onClick={this.add}>点击+1</button>
<button onClick={this.death}>卸载组件</button>
<button onClick={this.force}>不改变state强制更新forceUpdate</button>
</div>
);
}
//组件将要被卸载
componentWillUnmount(){
console.log("Count组件生命周期end 组件将要被卸载-componentWillUnmount");
}
//组件是否要更新,有返回值
//如果此方法不写,则react默认此方法返回值为true
//该函数相当于一个开关的效果 true-state更新继续流程,false-state更新,不进行下一步,最终组件不会更新
shouldComponentUpdate(){
console.log("Count组件生命周期 组件是否要render-shouldComponentUpdate");
return true;
}
//组件即将更新
componentWillUpdate(){
console.log("Count组件生命周期 组件即将render-componentWillUpdate");
}
//组件已经更新
componentDidUpdate(){
console.log("Count组件生命周期 组件已经render-componentDidUpdate");
}
}
//3.2 渲染组件到页面
ReactDOM.render(<Count/>, document.getElementById("root"));
</script>
</body>
</html>
(3)父组件render时,子组件相关钩子方法
- componentWillReceiveProps(){} 子组件接收父组件Props参数
- 示例代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>父组件A 子组件B</title>
</head>
<body>
<!--1.准备好容器-->
<div id="root"></div>
<!--2.引入js文件-->
<script type="application/javascript" src="../js/react.development.js"></script>
<script type="application/javascript" src="../js/react-dom.development.js"></script>
<script type="application/javascript" src="../js/babel.min.js"></script>
<script type="application/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
//3.1 创建组件
class A extends React.Component{
state = {carName:"奔驰"};
changeCar = ()=>{
this.setState({carName:"奥拓"});
}
render(){
return (
<div>
<h2>我是A组件,我的车为:{this.state.carName}</h2>
<button onClick={this.changeCar}>A中点击按钮</button>
<B carName={this.state.carName}/>
</div>
);
}
}
//子组件B
class B extends React.Component{
render(){
return (
<div>
<h2>我是B组件,接收到的车为:{this.props.carName}</h2>
</div>
);
}
//组件是否接收新的Props
componentWillReceiveProps(props){
console.log("组件B--componentWillReceivePros",props);
}
}
//3.2 渲染组件到页面
ReactDOM.render(<A/>, document.getElementById("root"))
</script>
</body>
</html>
- 页面初始化结果如下:
- 点击按钮,发现控制台打印了
- 注意:componentWillReceiveProps这个方法有个坑:
- 如果是页面初始化,这个方法在组件B中不执行;
- 当组件Arender的时候,组件B的 componentWillReceiveProps 才会执行,后面是组件A每次render的时候都会执行;
- 也就是说组件A执行render的方法的时候,作为从组件A中接收props参数的组件B才会执行下面一系列方法,
3. 组件的生命周期(旧)总结:
3.1组件生命周期(旧)分阶段
(1.) 初始化阶段
- 由ReactDOM.render()方法触发初次渲染,依次执行下面的方法
- constructor()
- componentWillMount()
- render()
- componentDidMount() -----常用,初始化操作,开启定时器、网络请求、订阅消息
(2.)更新阶段
- 情况① 由父组件render触发
- 情况② 组件自身state状态改变了
- shouldComponentUpdate()
- componentWillUpdate()
- render() -----常用,用的最多
- componentDidUpdate()
(3.)卸载阶段
- ReactDOM.unmountComponentNode()触发
- componentWillUnmount() -----常用,收尾操作,关闭定时器、取消消息订阅
4. 组件的生命周期(新)
4.1 生命周期(新)图
4.2 生命周期(新)3个钩子函数改名
- 在原来的名字前面加上前缀 UNSAFE_
- 原来的钩子函数还能用,但是控制台会有警告
- 在未来react18.x版本的时候,只有带上前缀 UNSAFE_ 的函数才能使用;
- 官方说明地址
- 三个函数分别是:
- componentWillMount ==> UNSAFE_componentWillMount 组件将要挂载
- componentWillReceiveProps ==> UNSAFE_componentWillReceiveProps 子组件接收父组件的新Props参数
- componentWillUpdate ==> UNSAFE_componentWillUpdate 组件将要更新
- 这3个钩子函数更名原因:
react正在开发异步渲染功能:
我们得到最重要的经验是,过时的组件生命周期往往会带来不安全的编码实践,具体函数如下:
componentWillMount
componentWillReceiveProps
componentWillUpdate
这些生命周期方法经常被误解和滥用;此外,我们预计,在异步渲染中,它们潜在的误用问题可能更大。我们将在即将发布的版本中为这些生命周期添加 “UNSAFE_” 前缀。(这里的 “unsafe” 不是指安全性,而是表示使用这些生命周期的代码在 React 的未来版本中更有可能出现 bug,尤其是在启用异步渲染之后。)
因此这种变化将是逐步的。我们目前的计划是:
16.3:为不安全的生命周期引入别名,UNSAFE_componentWillMount、UNSAFE_componentWillReceiveProps 和 UNSAFE_componentWillUpdate。(旧的生命周期名称和新的别名都可以在此版本中使用。)
未来 16.x 版本:为 componentWillMount、componentWillReceiveProps 和 componentWillUpdate 启用废弃告警。(旧的生命周期名称和新的别名都将在这个版本中工作,但是旧的名称在开发模式下会产生一个警告。)
17.0:删除 componentWillMount、componentWillReceiveProps 和 componentWillUpdate。(在此版本之后,只有新的 “UNSAFE_” 生命周期名称可以使用。)
4.3 生命周期(新)新增2个钩子函数
- 生命周期(旧)中3个钩子函数会在后期版本逐渐弃用,但是可以更名加前缀 UNSAFE_ 继续使用
- 新增2个钩子函数,基本不怎么使用
- getDerivedStateFromProps
- getSnapshotBeforeUpdate
4.3.1 getDerivedStateFromProps(props,state)
- 需要引入react17.x
- 示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>4-组件生命周期(新)</title>
</head>
<body>
<!--1.准备好容器-->
<div id="root"></div>
<!--2.引入js文件-->
<script type="application/javascript" src="../js/17.0.1/react.development.js"></script>
<script type="application/javascript" src="../js/17.0.1/react-dom.development.js"></script>
<script type="application/javascript" src="../js/17.0.1/babel.min.js"></script>
<script type="application/javascript" src="../js/17.0.1/prop-types.js"></script>
<script type="text/babel">
//3.1 创建组件
class Count extends React.Component{
//组件构造器
constructor(props) {
super(props);
console.log("Count组件生命周期1 组件构造器-constructor");
//初始化state
this.state = {count:0};
}
getDerivedStateFromProps(){
console.log("生命周期 新 getDerivedStateFromProps");
}
//按钮点击回调 add函数
add = ()=>{
//获取原状态
let count = this.state.count;
//求和并更新state的值
this.setState({count:count+1});
console.log(this.state.count);
}
//卸载组件
death = ()=>{
ReactDOM.unmountComponentAtNode(document.getElementById("root"))
}
//强制更新
force = ()=>{
console.log("Count组件生命周期 组件强制更新-forceUpdate");
this.forceUpdate()
}
//组件已经挂载
componentDidMount(){
console.log("Count组件生命周期4 组件已经挂载-componentDidMount");
}
//渲染虚拟dom
render(){
console.log("Count组件生命周期3 渲染虚拟dom-render");
return (
<div>
<h2>点击后总和={this.state.count}</h2>
<button onClick={this.add}>点击+1</button>
<button onClick={this.death}>卸载组件</button>
<button onClick={this.force}>不改变state强制更新forceUpdate</button>
</div>
);
}
//组件将要被卸载
componentWillUnmount(){
console.log("Count组件生命周期end 组件将要被卸载-componentWillUnmount");
}
//组件已经更新
componentDidUpdate(){
console.log("Count组件生命周期 组件已经render-componentDidUpdate");
}
}
//3.2 渲染组件到页面
ReactDOM.render(<Count/>, document.getElementById("root"));
</script>
</body>
</html>
- 页面初始化报错:
- 说明 getDerivedStateFromProps 方法不属于组件实例的,而是属于组件类的原型的
- 修改代码加上static定义方法
static getDerivedStateFromProps(){
console.log("生命周期 新 getDerivedStateFromProps");
}
- 页面初始化结果如下:
- 必须有返回值:
- stateObject
- null
- 如果返回 null
static getDerivedStateFromProps(){
console.log("生命周期 新 getDerivedStateFromProps");
return null;
}
- 页面初始化结果如下:
- 如果返回 stateObject ,代码修改如下
static getDerivedStateFromProps(){
console.log("生命周期 新 getDerivedStateFromProps");
return {count:99};
}
- 页面初始化结果如下:
- 点击 +1按钮,页面效果如下:
- 点击按钮 state中 count不在+1,count的值永远是99
- 也就是说 getDerivedStateFromProps方法的返回对象的属性值影响state中同名的属性值,并且会保持不变;
- getDerivedStateFromProps (props) 接收 props参数
//修改地方1
static getDerivedStateFromProps(props){
console.log("生命周期 新 getDerivedStateFromProps props=",props);
//直接返回接收的参数props
return props;
}
//修改地方2
ReactDOM.render(<Count count="199"/>, document.getElementById("root"));
- 页面初始化结果如下:
- 总结:这个方法使state值由props来决定
- 官方参考 getDerivedStateFromProps
- getDerivedFromProps(props,state) 可以接收两个参数 props state
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>4-组件生命周期(新)</title>
</head>
<body>
<!--1.准备好容器-->
<div id="root"></div>
<!--2.引入js文件-->
<script type="application/javascript" src="../js/17.0.1/react.development.js"></script>
<script type="application/javascript" src="../js/17.0.1/react-dom.development.js"></script>
<script type="application/javascript" src="../js/17.0.1/babel.min.js"></script>
<script type="application/javascript" src="../js/17.0.1/prop-types.js"></script>
<script type="text/babel">
//3.1 创建组件
class Count extends React.Component{
//组件构造器
constructor(props) {
super(props);
console.log("Count组件生命周期1 组件构造器-constructor");
//初始化state
this.state = {count:1};
}
static getDerivedStateFromProps(props,state){
console.log("生命周期 新 getDerivedStateFromProps props=",props);
console.log("生命周期 新 getDerivedStateFromProps state=",state);
//直接把props返回
return {count:props.count+state.count};
}
//按钮点击回调 add函数
add = ()=>{
//获取原状态
let count = this.state.count;
//求和并更新state的值
this.setState({count:count+1});
console.log(this.state.count);
}
//卸载组件
death = ()=>{
ReactDOM.unmountComponentAtNode(document.getElementById("root"))
}
//强制更新
force = ()=>{
console.log("Count组件生命周期 组件强制更新-forceUpdate");
this.forceUpdate()
}
//组件已经挂载
componentDidMount(){
console.log("Count组件生命周期4 组件已经挂载-componentDidMount");
}
//渲染虚拟dom
render(){
console.log("Count组件生命周期3 渲染虚拟dom-render");
return (
<div>
<h2>点击后总和={this.state.count}</h2>
<button onClick={this.add}>点击+1</button>
<button onClick={this.death}>卸载组件</button>
<button onClick={this.force}>不改变state强制更新forceUpdate</button>
</div>
);
}
//组件将要被卸载
componentWillUnmount(){
console.log("Count组件生命周期end 组件将要被卸载-componentWillUnmount");
}
//组件已经更新
componentDidUpdate(){
console.log("Count组件生命周期 组件已经render-componentDidUpdate");
}
}
//3.2 渲染组件到页面
ReactDOM.render(<Count count={199}/>, document.getElementById("root"));
</script>
</body>
</html>
- 页面初始化结果如下:
4.3.2 getSnapshotBeforeUpdate
- 示例代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>4-组件生命周期(新)</title>
</head>
<body>
<!--1.准备好容器-->
<div id="root"></div>
<!--2.引入js文件-->
<script type="application/javascript" src="../js/17.0.1/react.development.js"></script>
<script type="application/javascript" src="../js/17.0.1/react-dom.development.js"></script>
<script type="application/javascript" src="../js/17.0.1/babel.min.js"></script>
<script type="application/javascript" src="../js/17.0.1/prop-types.js"></script>
<script type="text/babel">
//3.1 创建组件
class Count extends React.Component{
//组件构造器
constructor(props) {
super(props);
console.log("Count组件生命周期1 组件构造器-constructor");
//初始化state
this.state = {count:0};
}
//让这个方法返回null
static getDerivedStateFromProps(props,state){
console.log("生命周期 新 getDerivedStateFromProps props=",props);
console.log("生命周期 新 getDerivedStateFromProps state=",state);
//直接把props返回
return null
}
getSnapshotBeforeUpdate(){
console.log("生命周期 新 getSnapshotBeforeUpdate");
}
//按钮点击回调 add函数
add = ()=>{
//获取原状态
let count = this.state.count;
//求和并更新state的值
this.setState({count:count+1});
console.log(this.state.count);
}
//卸载组件
death = ()=>{
ReactDOM.unmountComponentAtNode(document.getElementById("root"))
}
//强制更新
force = ()=>{
console.log("Count组件生命周期 组件强制更新-forceUpdate");
this.forceUpdate()
}
//组件已经挂载
componentDidMount(){
console.log("Count组件生命周期4 组件已经挂载-componentDidMount");
}
//渲染虚拟dom
render(){
console.log("Count组件生命周期3 渲染虚拟dom-render");
return (
<div>
<h2>点击后总和={this.state.count}</h2>
<button onClick={this.add}>点击+1</button>
<button onClick={this.death}>卸载组件</button>
<button onClick={this.force}>不改变state强制更新forceUpdate</button>
</div>
);
}
//组件将要被卸载
componentWillUnmount(){
console.log("Count组件生命周期end 组件将要被卸载-componentWillUnmount");
}
//组件已经更新
componentDidUpdate(){
console.log("Count组件生命周期 组件已经render-componentDidUpdate");
}
}
//3.2 渲染组件到页面
ReactDOM.render(<Count count={199}/>, document.getElementById("root"));
</script>
</body>
</html>
- 页面初始化结果如下:
- 这个方法必须有返回值
- null
- snapshot value 快照值 (可以是任意的值)
- 给 getSnapshotBeforeUpdate 方法添加返回值 null
getSnapshotBeforeUpdate(){
console.log("生命周期 新 getSnapshotBeforeUpdate");
return null;
}
- 输出结果如下:
- 给 getSnapshotBeforeUpdate 方法添加返回值 snapshot value 快照值
//修改位置 1
getSnapshotBeforeUpdate(prevProps, prevState){
console.log("生命周期 新 getSnapshotBeforeUpdate");
console.log("生命周期 新 getSnapshotBeforeUpdate prevProps=",prevProps);
console.log("生命周期 新 getSnapshotBeforeUpdate prevState=",prevState);
return "qq28129019";
}
// 修改位置 2
componentDidUpdate(prevProps,prevState,snapshot){
console.log("Count组件生命周期 组件已经render-componentDidUpdate");
console.log("生命周期 新 componentDidUpdate prevProps=",prevProps);
console.log("生命周期 新 componentDidUpdate prevState=",prevState);
console.log("生命周期 新 componentDidUpdate snapshot=",snapshot);
}
-
输出结果如下:
-
也就是说 getSnapshotBeforeUpdate()的返回值传递给 componentDidUpdate() 方法
-
完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>4-组件生命周期(新)</title>
</head>
<body>
<!--1.准备好容器-->
<div id="root"></div>
<!--2.引入js文件-->
<script type="application/javascript" src="../js/17.0.1/react.development.js"></script>
<script type="application/javascript" src="../js/17.0.1/react-dom.development.js"></script>
<script type="application/javascript" src="../js/17.0.1/babel.min.js"></script>
<script type="application/javascript" src="../js/17.0.1/prop-types.js"></script>
<script type="text/babel">
//3.1 创建组件
class Count extends React.Component{
//组件构造器
constructor(props) {
super(props);
console.log("Count组件生命周期1 组件构造器-constructor");
//初始化state
this.state = {count:0};
}
//让这个方法返回null
static getDerivedStateFromProps(props,state){
console.log("生命周期 新 getDerivedStateFromProps props=",props);
console.log("生命周期 新 getDerivedStateFromProps state=",state);
//直接把props返回
return null
}
getSnapshotBeforeUpdate(prevProps, prevState){
console.log("生命周期 新 getSnapshotBeforeUpdate");
console.log("生命周期 新 getSnapshotBeforeUpdate prevProps=",prevProps);
console.log("生命周期 新 getSnapshotBeforeUpdate prevState=",prevState);
return "qq28129019";
}
//按钮点击回调 add函数
add = ()=>{
//获取原状态
let count = this.state.count;
//求和并更新state的值
this.setState({count:count+1});
console.log(this.state.count);
}
//卸载组件
death = ()=>{
ReactDOM.unmountComponentAtNode(document.getElementById("root"))
}
//强制更新
force = ()=>{
console.log("Count组件生命周期 组件强制更新-forceUpdate");
this.forceUpdate()
}
//组件已经挂载
componentDidMount(){
console.log("Count组件生命周期4 组件已经挂载-componentDidMount");
}
//渲染虚拟dom
render(){
console.log("Count组件生命周期3 渲染虚拟dom-render");
return (
<div>
<h2>点击后总和={this.state.count}</h2>
<button onClick={this.add}>点击+1</button>
<button onClick={this.death}>卸载组件</button>
<button onClick={this.force}>不改变state强制更新forceUpdate</button>
</div>
);
}
//组件将要被卸载
componentWillUnmount(){
console.log("Count组件生命周期end 组件将要被卸载-componentWillUnmount");
}
//组件已经更新
componentDidUpdate(prevProps,prevState,snapshot){
console.log("Count组件生命周期 组件已经render-componentDidUpdate");
console.log("生命周期 新 componentDidUpdate prevProps=",prevProps);
console.log("生命周期 新 componentDidUpdate prevState=",prevState);
console.log("生命周期 新 componentDidUpdate snapshot=",snapshot);
}
}
//3.2 渲染组件到页面
ReactDOM.render(<Count count={199}/>, document.getElementById("root"));
</script>
</body>
</html>
4.4 新旧生命周期对比图
4.6 生命周期(新)新增钩子函数的示例:
- 初始化代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>5-生命周期新钩子方法的使用示例</title>
<style>
.list{
width:200px;
height:150px;
background-color: aquamarine;
overflow: auto;
}
.news{
height: 30px;
}
</style>
</head>
<body>
<!--1.准备好容器-->
<div id="root"></div>
<!--2.引入js文件-->
<script type="application/javascript" src="../js/17.0.1/react.development.js"></script>
<script type="application/javascript" src="../js/17.0.1/react-dom.development.js"></script>
<script type="application/javascript" src="../js/17.0.1/babel.min.js"></script>
<script type="application/javascript" src="../js/17.0.1/prop-types.js"></script>
<script type="text/babel">
class NewsList extends React.Component{
render(){
return (
<div className="list">
<div className="news">新闻7</div>
<div className="news">新闻6</div>
<div className="news">新闻5</div>
<div className="news">新闻4</div>
<div className="news">新闻3</div>
<div className="news">新闻2</div>
<div className="news">新闻1</div>
</div>
);
}
}
ReactDOM.render(<NewsList/>,document.getElementById("root"));
</script>
</body>
</html>
- 页面加载效果如下:
- 实现新增新闻的同时,界面显示的新闻不会自动下移:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>5-生命周期新钩子方法的使用示例</title>
<style>
.list{
width:200px;
height:150px;
background-color: aquamarine;
overflow: auto;
}
.news{
height: 30px;
}
</style>
</head>
<body>
<!--1.准备好容器-->
<div id="root"></div>
<!--2.引入js文件-->
<script type="application/javascript" src="../js/17.0.1/react.development.js"></script>
<script type="application/javascript" src="../js/17.0.1/react-dom.development.js"></script>
<script type="application/javascript" src="../js/17.0.1/babel.min.js"></script>
<script type="application/javascript" src="../js/17.0.1/prop-types.js"></script>
<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);
}
render(){
return (
<div className="list" ref="list">
{
this.state.newsArr.map((n,index)=>{
return <div key={index} className="news">{n}</div>
})
}
</div>
);
}
getSnapshotBeforeUpdate(){
return this.refs.list.scrollHeight;
}
componentDidUpdate(prevProps,prevState,snapshot){
this.refs.list.scrollTop += this.refs.list.scrollHeight-snapshot;
}
}
ReactDOM.render(<NewsList/>,document.getElementById("root"));
</script>
</body>
</html>
- 页面加载后效果:
- 新的新闻不断生成,但是页面并没有随着新闻生成而让最先生成的新闻顶下去
5.组件生命周期(新) 总结
5.1 生命周期的三个阶段
- 初始化阶段: 由ReactDOM.render()触发—初次渲染
- constructor()
- getDerivedStateFromProps
- render()
- componentDidMount()
- 更新阶段: 由组件内部this.setSate()或父组件重新render触发
- getDerivedStateFromProps
- shouldComponentUpdate()
- render()
- getSnapshotBeforeUpdate
- componentDidUpdate()
- 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount()
5.2 重要的勾子
- render:初始化渲染或更新渲染调用
- componentDidMount:开启监听, 发送ajax请求
- componentWillUnmount:做一些收尾工作, 如: 清理定时器
5.3 即将废弃的勾子
- componentWillMount
- componentWillReceiveProps
- componentWillUpdate
注意:现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。