likes
comments
collection
share

重识React — — 与生命周期叙叙旧

作者站长头像
站长
· 阅读数 12

前言

在2023年3月29日,React 18正式发布了,较之React 17,又带来了极为重磅的新特性,比如:

  1. ReactDOM.createRoot,支持并发模式的渲染。
  2. 支持setState自动批处理,将多个状态的更新批量处理,合并成一次更新。
  3. ....

咳咳,好像扯远了🤐,今天咱不是来聊React 18的。年轻的React 18站在Facebook大堂的舞台中心,无数的目光聚焦于她,一阵又一阵赞叹的声浪从四面八方涌向舞台,此起彼伏的人群中隐约可见一对互相依偎的身影,她们定定地望向舞台,目光里只有欣慰,她们是React 15React 16

React 15的生命周期

穿过热闹的人群,我走到她们的面前,站在右边的我认识,是React 16,她向我道了声好,算是久别重逢后的寒暄。站在左边的那位我虽然并不认识,但是开动脑筋想一想,也知道定是React 15了。

React 15是2016年推出的,那时候我还在高中写《五年高考三年模拟》呢,认不出来也是自然的。

React 15显得很热情,许是太久没和新面孔相遇了,招呼我再向她走近些儿,给我看了一张照片,一张足以道尽她的一生的照片。

名为“生命周期”的照片

  • constructor()

  • componentWillReceiveProps()

  • shouldComponentUpdate()

  • componentWillMount()

  • componentWillUpdate()

  • componentDidUpdate()

  • componentDidMount()

  • render()

  • componentWillUnmount()

这些字符凑在一起,像是一篇晦涩难懂的英语阅读理解😣,我的目光游移,实在难以聚焦,下意识地将照片翻过面来,凑巧地看到照片背面的那一行小字:“照着口诀念念看吧~”

“1个周期,3个阶段!”

刚念完口诀,照片背面竟显现出一幅流程图,我从来没有想到有一天看流程图能看出亲切感来😭...

组件挂载: 初始化渲染(Mounting)
constructor()
componentWillMount()
render()
componentDidmount()
组件卸载(Unmounting)
componentWillUnmount()
组件更新: 由父组件触发(Updating)
componentWillReceiveProps()
shouldComponentUpdate()
componentWillUpdate()
componentDidUpdate()
组件更新: 由组件自身触发(Updating)

结合这图,我算是明白了个大概,但是对于生命周期的执行顺序是否如图中所示般固定,还是没什么底。

React 15见我还是眉头紧皱,便体贴地说道:“小同学,不妨让我用JSX给你写几个Demo,帮助你理解吧?JSX你应该很熟了吧~”

我心虚地点点头:“哈哈...是挺熟的,麻烦了🙏”(昨天刚熟也算熟吧

Demo

父组件

import React from "react";
import LifeCycle from "./life-cycle";

class FatherOfLifeCycle extends React.Component<{}, any> {
  state: { text: string; hideChild: boolean } = {
    text: "父组件的文本",
    hideChild: false,
  };

  changeText = () => {
    this.setState({ text: "我是发生修改后的父组件文本" });
  };
  hideChildren = () => {
    this.setState({ hideChild: true });
  };
  render(): React.ReactNode {
    return (
      <div className="fatherContainer">
        <button onClick={this.changeText} className="changeText">
          点击修改父组件文本内容
        </button>
        <button onClick={this.hideChildren} className="hideChildren">
          隐藏子组件内容
        </button>
        {this.state.hideChild ? null : <LifeCycle text={this.state.text} />}
      </div>
    );
  }
}

export default FatherOfLifeCycle;

子组件

import React, { ReactDOM } from "react";

class LifeCycle extends React.Component<{ text: string }, any> {
  constructor(props: any) {
    console.log("进入constructor");
    super(props);
    this.state = { text: "子组件的文本" };
  }

  componentWillMount(): void {
    console.log("componentWillMount执行");
  }
  componentDidMount(): void {
    console.log("componentDidMount执行");
  }

  componentWillReceiveProps(nextProps: any, nextContext: any): void {
    console.log("componentWillReceiveProps执行");
  }

  shouldComponentUpdate(
    nextProps: any,
    nextState: any,
    nextContext: any
  ): boolean {
    console.log("shouldComponentUpdate执行");
    return true;
  }

  componentWillUpdate(nextProps: any, nextState: any, nextContext: any): void {
    console.log("componentWillUpdate执行");
  }

  componentDidUpdate(prevProps: any, prevState: any, snapshot?: any): void {
    console.log("componentDidUpdate执行");
  }
  componentWillUnmount(): void {
    console.log("子组件的componentWillUnmount执行");
  }

  changeText = () => {
    this.setState({ text: "我是发生修改后的子组件文本" });
  };

  render(): React.ReactNode {
    console.log("render方法执行");
    return (
      <div className="container">
        <button onClick={this.changeText} className="changeText">
          点击修改子组件文本内容
        </button>
        <p>子组件文本内容:{this.state.text}</p>
        <p>父组件文本内容:{this.props.text}</p>
      </div>
    );
  }
}

export default LifeCycle;

请忽略博主对于“Anyscript”的使用,以及时间紧凑,丑丑的css样式🥲

Mounting 组件的初始化渲染(挂载)

挂载只会在组件的生中发生1次。

组件挂载: 初始化渲染(Mounting)
constructor()
componentWillMount()
render()
componentDidmount()
  1. 注意🔈:☝️这里的render()在执行过程中并不会去操作真实DOM,而仅仅是把需要渲染的内容返回出来

  2. 挂载阶段真实DOM的渲染工作是由ReactDOM.render()承接的。

  3. componentDidmount()方法在渲染结束后被触发,这也意味是真实DOM已经挂载到了页面上,于是我们就可以在这个生命周期里执行真实DOM相关的操作

重识React — — 与生命周期叙叙旧

Updating
组件更新: 由父组件触发(Updating)
componentWillReceiveProps()
shouldComponentUpdate()
componentWillUpdate()
render
componentDidUpdate()
组件更新: 由组件自身触发(Updating)

提问❓:☝️这里的componentWillReceiveProps() 到底是由什么触发的?

回答

如果父组件导致组件重新渲染,即使props没有更改,也会调用此方法。

如果只想处理更改,请确保进行当前值与变更值的比较

—— React官方

也就是说,componentWillReceiveProps() 并不是由props的变化触发的,而是由父组件的更新触发的。

也是因此,当父组件因为其他与子组件无关的属性变化导致更新时,我们期望避免这种对子组件不必要的渲染的发生,因此React提供给了我们shouldComponentUpdate()

React组件会根据shouldComponentUpdate()的返回值来决定是否接着执行该方法之后的生命周期,进而决定是否对组件进行re-render。

因此,除了避免不需要的渲染发生,我们也可以往其中加一些判定条件,来主动实现一些我们期望的渲染发生。退可守也进可攻。

思考❓:那么PureComponent是不是也是上述功能的一种体现呢?

期待你在评论区的留言👀

重识React — — 与生命周期叙叙旧

Unmounting
组件卸载(Unmounting)
componentWillUnmount()

组件销毁的常见原因,有以下两个:

  1. 组件在父组件中被移除了
  2. 组件中设置了key属性,父组件在render的过程中,发现key值和上一次不一致

重识React — — 与生命周期叙叙旧

好用,但又没那么好用

在React 15的自我介绍下,我大致上是明白了。但是一路听下来,有两个地方觉得不太对劲,componentWillMount()componentWillUpdate()两兄弟好像有点多余?如同给手机套了两层手机壳,手机摔地上时,确实会比套一层的手机受伤小一点,但是仅仅为了这微小的提升,导致整机多了一倍的重量,算来算去都觉得不划算啊。

我将这个疑问与她们分享,React 16听完后对我是又好气又好笑,抱怨道:“你之前用了这么久的React 16,难道不觉得componentWillMount()componentWillUpdate()从来都没见过吗!”

我一拍脑袋,确实啊,为什么React 16里没有它们了呢?

React 16也拍了拍我的脑袋,然后清了清嗓子,一副早就有话要说的样子...

后言

还剩10分钟不到,就要过凌晨12点啦,每日一文可不能断咯😩。因为今日周一加班比较晚,所以本文的内容有些仓促,也有很多不足(下次一定修改)。 那就先聊到这里吧,我是海石,我们明天见~