【React】生命周期详解

文章目录

    • 一、生命周期钩子函数
      • 1.1、旧版钩子函数【淘汰】
      • 1.2、新版钩子函数【新增】
      • 1.3、常用的钩子函数
    • 二、生命周期各阶段
      • 2.1、挂载阶段
        • render【!】
        • constructor【!】
        • componentDidMount【!】
        • static getDerivedStateFromProps【!】
      • 2.2、更新阶段
        • componentDidUpdate【!】
        • shouldComponentUpdate
        • getSnapshotBeforeUpdate
      • 2.3、卸载阶段
        • componentWillUnmount【!】
      • 2.4、错误处理
        • componentDidCatch
        • static getDerivedStateFromError

一、生命周期钩子函数

  • React 的生命周期从广义上分为三个阶段:挂载、渲染、卸载
  • 因此可以把 React 的生命周期分为两类:挂载卸载过程和更新过程

1.1、旧版钩子函数【淘汰】

  • 了解即可,因为现在一般都是用新版的,但是有些公司以前的项目是用旧版本写的,这时你就需要知道里面一些钩子函数是做什么用的
  • 16.3之前的版本生命周期图示:

在这里插入图片描述

  • 旧版本独有的,17.0版本已经删除了的钩子函数(17.0之前的版本仍可以继续使用):
  • componentWillMountcomponentWillReceivePropscomponentWillUpdate
  • 在使用这几个钩子函数时,前面要加UNSAFE_

import React ,{Component} from 'react'
import ReactDOM  from 'react-dom'
// 子组件
class Child extends Component{// 初始化数据constructor(props){super(props);this.state = {};}// 当组件接收到新的props值会触发,新版已经淘汰UNSAFE_componentWillReceiveProps(){console.log("componentWillReceiveProps 运行了");}render(){return(<div>子节点:{this.props.num}</div>)}}
class App extends Component {constructor(props) {console.log('constructor 运行了');super(props);this.state = {num:0};}// 组件挂载前,新版已经淘汰了UNSAFE_componentWillMount(){console.log('componentWillMount 运行了')}// 箭头函数changeNum = ()=>{this.setState({num:this.state.num+1})}// 渲染数据render() {console.log('render 运行了');return (<div>根节点<br/>{/* 
必须加/
*/
}<button onClick={this.changeNum}>++</button>{this.state.num}<br/>子组件:<Child num={this.state.num}/></div>);}// 组件更新前,新版已经淘汰了UNSAFE_componentWillUpdate(){console.log('componentWillUpdate 运行了');} } ReactDOM.render(<App />,document.getElementById('root') )

在这里插入图片描述
在这里插入图片描述

1.2、新版钩子函数【新增】

  • 16.3版本生命周期图示:
    在这里插入图片描述

  • 16.4及以上版本生命周期图示:
    在这里插入图片描述

  • 增加了:static getDerivedStateFromProps()getSnapshotBeforeUpdate()

  • 用来替代被弃用的旧版本那三个钩子函数

  • 还增加了:static getDerivedStateFromError()componentDidCatch()

  • 用来处理错误


import React ,{Component } from 'react'
import ReactDOM  from 'react-dom'
class App extends Component {constructor(props) {console.log('constructor 运行了');super(props);this.state = {count: 0};}// 一般用不到:通过props设置获取到 state的值  // getDerivedStateFromProps前面要加上static保留字,声明为静态方法,不然会被react忽略掉// getDerivedStateFromProps里面的this为undefinedstatic getDerivedStateFromProps(nextProps, prevState) {console.log("getDerivedStateFromProps 运行了");return true;}// 一般用不到:滚动的特殊处理,与componentDidUpdate 配对使用的/*  在react `render()`后的输出被渲染到DOM之前被调用。它使您的组件能够在它们被潜在更改之前捕获当前值(如滚动位置)。这个生命周期返回的任何值都将作为参数传递给componentDidUpdate()*/ getSnapshotBeforeUpdate(prevProps, prevState) {console.log("getSnapshotBeforeUpdate 运行了");return 11;}componentDidUpdate(prevProps, prevState, snapState) {console.log("componentDidUpdate 运行了", snapState);}// 本身或后代有报错会触发componentDidCatch(error, info) {console.log("componentDidCatch 运行了");}// 后代组件抛出错误后会触发static getDerivedStateFromError(nextProps, prevState) {console.log("getDerivedStateFromError 运行了");}// 定义事件changeCount = () => {this.setState({ count: this.state.count + 1 })}// 渲染数据render(){const { count} = this.state;return(<div><button onClick={this.changeCount}>++ {count}</button></div>)}
}
ReactDOM.render(<App />,document.getElementById('root')
)

在这里插入图片描述
在这里插入图片描述

1.3、常用的钩子函数

  • 下面的钩子函数在新老版本都是比较常用的
import React, { Component } from 'react';
class App extends Component {//初始化  ,state  ,props,ref,绑定thisconstructor(props) {super(props);this.state = {}}//渲染  ,父组件 执行了渲染,默认子组件的渲染函数也要执行render() {return (<div>首页</div>);}//是否需要更新  ,不需要 返回 false   ;需要 返回trueshouldComponentUpdate() {return true;}// DOM操作,实例化,数据请求//异步处理,有副作用的处理  ;获取数据、执行 setTimeout  setIntervalcomponentDidMount() {console.log('componentDidMount')}//更新时的钩子函数componentDidUpdate() {console.log('componentDidUpdate')}//卸载 回收操作  定时器  解除绑定componentWillUnmount() {// 类似于vue 的beforeDestoryconsole.log('componentDidUpdate')}
}export default App;

  • componentDidMount 案例:实现文字透明度的渐变效果
import React ,{Component} from 'react'
import ReactDOM  from 'react-dom'
class App extends Component{constructor() {super();this.state = {opacity: 1  // 不透明}}//在这个钩子函数中可以获取数据componentDidMount(){setInterval(()=>{//报错,this指向不对,目前指向了window,可以使用箭头函数let myOpacity = this.state.opacity; myOpacity -= 0.05;//如果透明度到0,重新置1if(myOpacity<=0){myOpacity = 1;}this.setState({opacity:myOpacity})},500);}render() {return (<><h1 style={{"opacity": this.state.opacity}}>hello world</h1></>);}
}
ReactDOM.render(<App />,document.getElementById('root')
)

在这里插入图片描述

二、生命周期各阶段

React中组件也有生命周期,也就是说也有很多钩子函数供我们使用, 组件的生命周期,我们会分为四个阶段,初始化(挂载)、运行中(更新)、销毁(卸载)、错误处理(异常)(16.3之后)

常用的生命周期方法就下面几个,为啥?因为好记,好记才会常用。程序员的本性。
其实其他生命周期方法也好用,但是需要特地条件下使用才会觉得方便。所以就不太常用

  • constructor()
  • render()
  • componentDidMount()
  • componentDidUpdate()
  • componentWillUnmount()
  • getDerivedStateFromProps

2.1、挂载阶段

  • 当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:
  1. constructor
  2. static getDerivedStateFromProps
  3. render
  4. componentDidMount
render【!】
  • render()
  • 调用:在组件挂载之前
  • 是 class 组件中唯一必须实现的方法

使用方法

  • 当他被调用时,他将计算this.propsthis.state,并返回以下一种类型:
返回的类型描述
React元素通常通过 JSX 创建,比如DOM元素或组件
数组 或 fragments使得 render 方法可以返回多个元素。
Portals可以渲染子节点到不同的 DOM 子树中
字符串或数值它们在 DOM 中会被渲染为文本节点
null 或 布尔值什么都不渲染。

使用限制

  • render()方法必须是一个纯函数
  • 他不能改变state
  • 也不能直接和浏览器进行交互
  • 应该将事件放在其他生命周期函数中

注意:

  • 如果shouldComponentUpdate()返回false
  • 则不会调用 render()。
constructor【!】
  • constructor(props)
  • 调用:在组件挂载之前
constructor(props) {super(props);this.state = {isLiked: props.isLiked};
}

constructor使用条件

  • 在添加其他内容前
  • 调用super(props)
  • 将父组件传来的props绑定到这个类中
  • 不然this.props将会无法得到

constructor作用

  • 通过给 this.state 赋值对象来初始化内部 state
  • 为事件处理函数绑定实例

constructor()在以下条件可以不用:

  • 不初始化 state
  • 不进行方法绑定
  • 组件内全是纯函数写法。

constructor()中不要这样做:

  • 调用 setState() 方法
  • 引入任何副作用或订阅
componentDidMount【!】
  • componentDidMount()
  • 调用:组件挂载到DOM后,只会被调用一次
  • 初始化使得DOM节点应该进行到这里。
  • 这个方法是用于在服务器渲染上的唯一方法
  • 这个方法因为是在渲染之前被调用,也是惟一一个可以直接同步修改state的地方。

  • 通常在这里进行ajax请求
  • 如果要初始化第三方的dom库,也在这里进行初始化。只有到这里才能获取到真实的dom.
static getDerivedStateFromProps【!】
  • static getDerivedStateFromProps(nextProps, prevState)
  • 其周期函数是静态方法,务必加上static关键词;
  • 在组件创建时和更新时的render方法之前调用
  • 返回一个对象来更新状态,或者返回null来不更新任何内容。

  • Father.jsx
import React, { Component } from "react";
import Son from "./Son";
class Father extends Component {render() {return (<div><Son val={1423}></Son></div>);}
}export default Father;
  • Son.jsx
import React, { Component } from "react";class Son extends Component {//  初始化状态state = {num: 0,a: 0};render() {return (<div><div>子组件:当前自身的state是{this.state.num}</div><button onClick={this.ck.bind(this)}>给a+1</button></div>);}// 场景:希望本组件自身的状态与props同步,这个时候就可以使用getDerivedStateFromProps周期static getDerivedStateFromProps(props, state) {console.log('走了xxxxx');// 在这里进行赋值if (props.val === state.num) {// 相等,不需要赋值return null;} else {// 不相等,则需要赋值return {num: props.val,};}}ck() {this.setState((state) => {return {a: state.a + 1,};});}
}export default Son;
  • 运行结果
    在这里插入图片描述

2.2、更新阶段

  • 当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:
  1. static getDerivedStateFromProps
  2. shouldComponentUpdate
  3. render
  4. getSnapshotBeforeUpdate
  5. componentDidUpdate
componentDidUpdate【!】
  • componentDidUpdate(prevProps, prevState, snapshot)
  • 调用:在组件更新之后
  • 注意:首次渲染不会执行此方法。

componentDidUpdate()用法

  • 当组件更新时,可以在此处对 DOM 进行操作。
  • 如果你对更新前后的 props 进行了比较,也可以选择在此处进行网络请求
  • 例如,如果props没有改变,则可能不需要网络请求
  • 可以在里面直接调用 setState()
  • 但是 setState() 得在条件语句里使用
  • 避免导致额外的重新渲染

与componentDidUpdate()联用

  • 如果组件实现getSnapshotBeforeUpdate()生命周期
  • 则它返回的值将作为第三个参数【snapshot】传递给componentDidUpdate()
  • 否则,这个参数是undefined

与shouldComponentUpdate()联用

  • 如果 shouldComponentUpdate() 返回值为 false
  • 则不会调用 componentDidUpdate()。
shouldComponentUpdate
  • shouldComponentUpdate(nextProps, nextState)
  • 根据 shouldComponentUpdate() 的返回值
  • 判断组件的输出是否受stateprops的影响。
  • 默认行为是 state 每次发生变化组件都会重新渲染。

  • 当 props 或 state 发生变化时
  • shouldComponentUpdate会在渲染执行之前被调用
  • 返回值默认为 true。
  • 首次渲染或使用 forceUpdate() 时不会调用该方法。

shouldComponentUpdate() 返回 false

  • componentWillUpdaterender
  • componentDidUpdate不会被调用。
getSnapshotBeforeUpdate
  • getSnapshotBeforeUpdate(prevProps, prevState)
  • 在最近一次渲染输出(提交到 DOM 节点)之前调用
  • 它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)
  • 这个生命周期返回的任何值都将作为参数传递给componentDidUpdate()。

作用地点:

  • 此用法并不常见,但它可能出现在 UI 处理中
  • 如需要以特殊方式处理滚动位置的聊天线程等。
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>);}
}

2.3、卸载阶段

  • 当组件从 DOM 中移除时会调用如下方法:
componentWillUnmount【!】
  • componentWillUnmount()
  • 调用:在组件卸载之前
  • 作用:执行一些清理工作
  • 例如
  • 清除组件中使用的定时器,
  • 取消网络请求或清理在 componentDidMount 中创建的任何监听
  • 清除 componentDidMount 中手动创建的DOM元素

2.4、错误处理

  • 当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:
componentDidCatch
  • componentDidCatch(error, info)
  • 此生命周期在后代组件抛出错误后被调用。
  • error —— 抛出的错误。
  • info —— 带有 componentStack key 的对象
  • componentDidCatch() 会在“提交”阶段被调用,因此允许执行副作用。
  • 它应该用于记录错误之类的情况
static getDerivedStateFromError
  • static getDerivedStateFromError(error)
  • 此生命周期会在后代组件抛出错误后被调用
  • 它将抛出的错误作为参数,并返回一个值以更新 state


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部