react学习——生命周期

组件的生命周期

生命全周期图
React中组件也有生命周期,也就是说也有很多钩子函数供我们使用, 组件的生命周期,我们会分为四个阶段,初始化、运行中、销毁、错误处理

1.初始化(挂载)

在初始化阶段(当组件实例被创建并插入DOM中时)会执行下面的生命周期
1. constructor
2. static getDerivedStateFromProps()
3. render()
4. componentDidMount()

各生命周期详解

(1) constructor(props)

React组件的构造函数在挂载之前被调用。
在实现React.Component构造函数时,需要先在添加其他内容前,调用super(props),用来将父组件传来的props绑定到这个类中,使用this.props将会得到。

官方建议不要在constructor引入任何具有副作用和订阅功能的代码,这些应当使用componentDidMount()

constructor中应当做些初始化的动作,如:初始化state,将事件处理函数绑定到类实例上,但也不要使用setState()

  • 一般在构造函数里做什么事?
  • 初始化状态
  • 创建ref
  • 绑定事件的this指向
constructor(props){
    super(props)
    console.log("1---执行构造函数")
    this.state={
      msg:'(*^_^*)'
    }
    this.inputRef = React.createRef()
    this.handleClick = this.handleClick.bind(this)
  }

(2) static getDerivedStateFromProps(nextProps,prevState)

getDerivedStateFromProps 会在调用 render方法之前调用,并且在初始挂载及后续更新时都会被调用。

  • 它应返回一个对象来更新 state ,如果返回null 则不更新任何内容。
    生命全周期图
    参数解释:
  • props 是父组件传来的,有初始化的值就赋给这个state
  • return 返回出去的值 会和 state中的值进行合并
  • 例子:
static getDerivedStateFromProps(props, state) {
    // props 父组件传来的,有初始化的值,赋给这个 state
    // 返回的值 会和 state 中的值进行合并
    return {
      num: state.num || 1,
    }
  }

使用场景:

  • 无条件的根据 prop 来更新内部 state,也就是只要有传入 prop 值, 就更新 state
  • 只有 prop 值和 state 值不同时才更新 state 值。

(3) render()

render方法是必须的。当他调用的时候,将计算this.propsthis.state,并一定要有 返回return
返回的可以是下面的几种类型

  • React元素。通过jsx创建,既可以是dom元素,也可以是用户自定义的组件。
  • 字符串或数字。他们将会以文本节点形式渲染到dom中。
  • Portals。react 16版本中提出的新的解决方案,可以使组件脱离父组件层级直接挂载在DOM树的任何位置。
  • null,什么也不渲染
  • 布尔值。也是什么都不渲染。
    当返回null,false,ReactDOM.findDOMNode(this)将会返回null,什么都不会渲染。
    注意:
  • render()方法必须是一个纯函数,他不应该改变state`,也不能直接和浏览器进行交互,应该将事件放在其他生命周期函数中。
    • 在render()里面调用setState()方法时,会重新渲染 render()方法,造成死循环。
  • 如果shouldComponentUpdate()返回falserender()不会被调用。

(4) componentDidMount

componentDidMount组件被装配后立即调用。初始化使得DOM节点应该进行到这里。

通常在这里进行ajax请求

如果要初始化第三方的dom库,也在这里进行初始化
只有这里才能获取到真实的dom

例子: 一个简单的小demo

import React, { Component } from 'react'
// 生命周期
export default class App extends Component {
  constructor(props) {
    super(props)
    this.state = {}
    console.log("1---执行构造函数")
  }
  // 从父组件中提取状态
  static getDerivedStateFromProps(props, state) {
    // props 父组件传来的,有初始化的值,赋给这个 state
    // 返回的值 会和 state 中的值进行合并
    console.log("2---执行getDerivedStateFromProps")
    return {
      num: state.num || 1,
    }
  }
  // 告诉大家我已经挂载到 树上了
  componentDidMount() {
    console.log("4---执行componentDidMount");
    // 可以做哪些事情
    // 设置一些定时器
    // 发送网络请求
    // 订阅事件
  }
  render() {
    console.log("3---执行 render函数");
    // 在render中不能 改变状态,会死循环!!!
    return (
      <div>
        <span>{this.state.num}</span>
        <button>1</button>
      </div>
    )
  }
}

2.更新阶段

props或state的改变可能会引起组件的更新,组件重新渲染的过程中会调用以下方法:

  1. static getDerivedStateFromProps()
  2. shouldComponentUpdate()
  3. render()
  4. getSnapShotBeforeUpdate()
  5. componentDidUpdate()

各生命周期详解

(1) shouldComponentUpdate(nextProps,nextState)

根据shouldComponentUpdate()的返回值,判断React组件的输出是否受当前 state 或 props 更改的影响
默认行为是 state每次发生变化 ,组件都会重新渲染。

  • 当 props 或 state 发生变化时,shouldComponentUpdate()会在渲染执行之前被调用
  • 返回值默认为true
  • 首次渲染或使用forceUpdate()时不会调用该方法
  • 此方法 是做 性能优化 -> 内置的PureComponent 组件
    • PureComponent 会对 props 和 state 进行浅层比较,并减少了跳过必要更新的可能性。

结合下面的组件状态更新查看此时的例子:PureComponent 组件

  • 父组件更新的时候,子组件更新了,但没必要,要减少更新,性能优化,
  • PureComponent 在 shouldComponentUpdate
    浅比较 值比较值,地址比较地址,地址里面如果内容变化,它不知道.
  • 如果组件值没有变化,就不会更新
import React, { Component, PureComponent } from "react";
// 生命周期 组件中有组件怎么执行顺序,同理执行
// 高性能组件 继承 PureComponent
class Demo extends PureComponent {
  componentDidUpdate() {
    console.log("Demo 组件更新了");
  }
  render() {
    return <h3>Demo子组件</h3>;
  }
}
export default class App extends Component {
  state = {
    num: 1,
  };
  componentDidUpdate() {
    console.log("App 组件更新了");
  }
  render() {
    return (
      <div>
        <h1>App父组件</h1>
        <h1>{this.state.num}</h1>
        <Demo></Demo>
        <button
          onClick={() => {
            this.setState({
              num: this.state.num + 1,
            });
          }}
        >
          父组件更新
        </button>
      </div>
    );
  }
}

初始界面是:
在这里插入图片描述

  • Demo组件继承PureComponent时,子组件中值没有变化,他就不更新。
    在这里插入图片描述

  • Demo组件继承 Component时,子组件没有变化,它还是更新。
    在这里插入图片描述

  • 如果你一定要手动编写此函数,可以将 this.props 与 nextProps 以及 this.state 与nextState 进行比较,并返回 false 以告知 React 可以跳过更新。请注意,返回 false 并不会阻止子组件在 state 更改时重新渲染。

  • 可以查看下面的 组件更新的第一种方式 —— 属性更新的例子

(2) getSnapShotBeforeUpdate(prevProps,prevState)

  • 在最近一次渲染输出(提交到DOM节点)之前调用。
    它使得组件能在发生更改之前从DOM中捕获一些信息(例如,滚动位置)
    此生命周期的任何返回值将作为参数传递给 componentDidUpdate()
    此用法并不常见,但它可能出现在UI处理中,如需要以特殊方式处理滚动位置的聊天记录等
  • 应返回snapshot的值(或null)

(3) componentDidUpdate(prevProps,prevState,snapshot)

  • 会在更新后被立即调用。首次渲染不会执行此方法。
  • 当组件更新后,可以在此处对DOM进行操作。如果你对更新前后的 props 进行了比较,也可以选择在此处进行网络请求
    • 例如,当 props 未发生变化时,则不会执行
  • 如果组件实现了 getSnapshotBeforeUpdate() 生命周期(不常用),则它的返回值将作为 componentDidUpdate() 的第三个参数 “snapshot” 参数传递。否则此参数将为 undefined。
注意

如果 shouldComponentUpdate()返回值为 false ,则不会调用 componentDidUpdate()

组件更新时的三种方式:

  1. new Props 新属性
  2. setState() 新状态
  3. forceUpdate 强制更新

例子1:新属性更新的时候

import React, { Component } from "react";
// 属性更新
// 父子之间传值
class Demo extends Component {
  render() {
    return <h1>{this.props.msg}子组件</h1>;
  }
  shouldComponentUpdate(nextProps, nextState) {
  	// 比较更新前后的值是否一致,一致则不更新
    if (this.props.msg === nextProps.msg) {
      return false;
    } else {
      return true;
    }
  }
  componentDidUpdate() {
    console.log("Demo子组件更新了");
  }
}
export default class App extends Component {
  state = {
    num: 1,
  };
  componentDidUpdate() {
    console.log("App组件更新了");
  }
  render() {
    return (
      <div>
        <h1>App组件</h1>
        <Demo msg={this.state.num}></Demo>
        <button
          onClick={() => {
            this.setState({
              num: this.state.num + 1, // 这是组件Demo更新
              // num:1, // 这是组件Demo不更新
            });
          }}
        >
          更新子组件
        </button>
      </div>
    );
  }
}

例子2:新状态更新的时候

import React, { Component } from 'react'
// 组件更新时 三种方式
// 新属性 new Props
// 新状态 setState state  ==> Vue data
// 强制更新 forceUpdate

// 下面是 状态更新 的情况
export default class App extends Component {
  state={
    msg:"😀"
  }
  // 这个是静态的
  // 返回一个对象来更新,合并state,如果返回 null 则不更新任何内容
  static getDerivedStateFromProps(props,state){
    // 挂载的时候 更新的时候 都会调用
    console.log("1-App组件更新 getDerivedStateFromProps");
    return null
  }
  // 这个没有static  如果返回false 截止->视图上没有更新
  shouldComponentUpdate(nextProps,nextState){
    console.log("2-App组件更新 shouldComponentUpdate");
    // return false //不会更新
    return true //会更新,不写这个函数,默认返回true
  }
  // 在最近一次渲染输出(提交到DOM节点)之前调用。
  // 它使得组件能在发生更改之前从DOM中捕获一些信息(例如,)
  getSnapshotBeforeUpdate(nextProps,nextState){
    // 返回的结果作为 componentDidUpdate 第三个参数传入
    console.log("4-App组件更新 getSnapshotBeforeUpdate");
    return null
  }
  componentDidUpdate(nextProps,nextState,snapshot){
    console.log("5-App组件更新 componentDidUpdate");
    console.log(snapshot);
    return null
  }
  render() {
    console.log("3-App组件更新 render");
    return (
      <div>
        <h1>{this.state.msg}</h1>
        <button onClick={()=>{
          this.setState({
            msg:"⭐"
          })
        }}>改变表情</button>
      </div>
    )
  }
}
  • 初始界面
    在这里插入图片描述

  • 点击后界面
    在这里插入图片描述

  • 按上述顺序执行

但是:当shouldComponentUpdate()返回 false 时

  shouldComponentUpdate(nextProps,nextState){
    console.log("2-App组件更新 shouldComponentUpdate");
    return false //不会更新
    // return true //会更新,不写这个函数,默认返回true
  }
  • 点击后的页面是
    shouldComponentUpdate
新状态更新的时 —— 滚动的例子:
import React, { Component } from "react";
// 例子:点击跳转到指定的滚动位置
// 2-引入大小盒子的样式
import "./App.css"
export default class App extends Component {
  // 5-在构造函数中创建 ref
  constructor(props){
    super(props)
    this.boxRef = React.createRef()
  }
  state = {
    msg: "😀",
  };
  static getDerivedStateFromProps(props, state) {
    console.log("1-App组件更新 getDerivedStateFromProps");
    return null;
  }
  shouldComponentUpdate(nextProps, nextState) {
    console.log("2-App组件更新 shouldComponentUpdate");
    return true;
  }
   // 返回的结果 会作为 componentDidUpdate 第三个参数传入 (例如,滚动位置)
  getSnapshotBeforeUpdate(nextProps, nextState) {
    console.log("4-App组件更新 getSnapshotBeforeUpdate");
    return 500;
  }
  componentDidUpdate(nextProps, nextState, snapshot) {
    console.log("5-App组件更新 componentDidUpdate");
    // 6-在这里,组件更新后 获取外部盒子节点
    console.log(this.boxRef.current);
    // 7-给外部盒子添加 滚动
    this.boxRef.current.scrollTop = snapshot
    return null;
  }
  render() {
    console.log("3-App组件更新 render");
    return (
      // 1-滚动条件:内部盒子高于外部盒子
      // 4-是控制外部盒子的滚动条的位置,所以要获取外部盒子 这个节点 使用 ref绑定
      <div className="outerBox" ref={this.boxRef}>
        <div className="innerBox">
          <h1>{this.state.msg}</h1>
          {/* 3-设置事件当点击的时候,滚动条跳转到指定位置 */}
          <button
            onClick={() => {
              this.setState({
                msg: "⭐",
              });
            }}
          >
            改变表情
          </button>
        </div>
      </div>
    );
  }
}
  • 开始界面

例子3:强制更新的时候

就是一个forceUpdate()方法。

import React, { Component } from 'react'
// 组件更新的第三种方式
// 强制更新 forceUpdate
export default class App extends Component {
  // 2-定义事件 调用forceUpdate()方法
  handleForceUpdate=(params) => {
    this.forceUpdate(()=>{
      console.log("调用forceUpdate()强制更新");//2
    })
  }
  // 3-这里观看有没有更新
  componentDidUpdate(){
    console.log("组件更新了"); //1
  }
  render() {
    return (
      <div>
        {/* 1-点击事件触发函数 */}
        <button onClick={this.handleForceUpdate}>强制更新</button>
      </div>
    )
  }
}

3.卸载阶段

1. componentWillUnmount()

生命周期详解

(1) componentWillUnmount()

会在组件卸载及销毁之前直接调用。
在此方法中执行必要的清理操作
例如:

  • 清除timer
  • 取消网络请求
  • 清除在componentDidMount()中创建的订阅

a 卸载时简单例子

放在卸载了的组件中,才执行
卸载之后再html中就不显示了

import React, { Component } from "react";
// 定义了一个子组件 Demo
class Demo extends Component {
  // 哪个组件销毁了,哪个里面就执行这个
  componentWillUnmount(){
    console.log("组件卸载了");
  }
  render() {
    return <h1>Demo组件</h1>;
  }
}
export default class App extends Component {
  state = {
    show: true,
  }
  // 点击事件 改变show的值
  handleClick=(params) => {
    this.setState({
      show:false
    })
  }
  render() {
    console.log("render执行了");
    return (
      <div>
        <h1>App组件</h1>
        {/* <Demo></Demo> */}
        {/* 动态卸载Demo */}
        {/* 依据show的值来判断 显示 何 */}
        {this.state.show ? <Demo></Demo> : "Demo卸载了"}
        <button onClick={this.handleClick}>点击卸载Demo组件</button>
      </div>
    );
  }
}

b 组件卸载阶段的一些操作

  1. 清楚定时器
  2. 取消网络请求(安装了axios)
  3. 取消订阅
import React, { Component } from 'react'
// componentWillUnmount()会在组件卸载及销毁之前直接调用,在此方法中执行必要的清理操作
// 组件卸载阶段做的一些事
// 1.清除定时器
// 2.取消网络请求, 网络请求要来引入axios
import axios from 'axios'
// 3.清除在componentDidMount()中创建的订阅
class Demo extends Component {
  constructor(props) {
    super(props)
    // b-1-1创建CancelToken
    let CancelToken = axios.CancelToken
    // b-1-2调用CancelToken的dource方法
    this.source = CancelToken.source()
  }
  // 在组建挂载的时候做的是组件卸载相反的事
  componentDidMount() {
    /* a-1设置定时器 */
    this.timerId = setInterval(function () {
      console.log("定时器运行了");
    }, 1000)
    /* b-1 axios设置网络请求 */
    axios.get("https://apimusic.linweiqin.com/search?keywords=%27%E7%83%AD%E9%97%A8%27", {
      // b-1-3看状态里
      cancelToken: this.source.token
    }).then(res => {
      console.log(res);
    })
    /* c-1 设置订阅事件 */
    window.addEventListener('click', this.handleClick)
  }
  componentWillUnmount() {
    /* a-2 清除定时器 */ //此时要给定时器设置一个名称
    clearInterval(this.timerId)
    console.log("Demo组件卸载了");
    /* b-2 怎么取消网络请求???? */ //使用source.cancel
    this.source.cancel('取消本次请求了')
    /* c-2 取消订阅 */ //移除事件
    window.removeEventListener('click', this.handleClick)
  }
  handleClick = () => {
    console.log("window被点击了");
  }
  render() {
    return <h1>Demo组件🐖</h1>
  }
}
export default class App extends Component {
  state = {
    show: true
  }
  render() {
    return (
      <div>
        <h1>App组件</h1>
        {this.state.show ? <Demo></Demo> : ""}
        <button onClick={() => {
          this.setState({
            show: false
          })
        }}>点击卸载</button>
      </div>
    )
  }
}

问:CancelToken是干嘛用的??

先占张图,明天再看
在这里插入图片描述

4.错误处理

  1. componentDidCatch()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值