react hooks 生命周期渲染时机简述

2022-10-31 10:38:54 浏览数 (1)

使用hooks 已经有一段时间了,虽然团队都已经可以熟练应用到项目,但是没有深入理解hooks 的意思。state , useEffect 滥用,造成了多余的多次渲染。

实战例子

通过一个实现来了解下调用一个组件的一生,先看一下整个demo的样子。

整个结构是父组件调用红框子组件,子组件有一个title 是父组件传过来的属性,另一个subtitle 是一个state 按钮是刷新这个state 。

父组件代码

代码语言:javascript复制
 <div className={styles.container}>
        <Button
          type="primary"
          onClick={() => {
            setTitle((c) => {
              return c   'change';
            });
          }}
        >
          刷新子组件title
        </Button>
        <Button
        style={{"marginLeft":'30px'}}
          type="primary"
          onClick={() => {
            setVisible(false);
          }}
        >
          卸载子组件
        </Button>

        {visible && <ReturnView title={title}></ReturnView>}
      </div>
子组件代码
代码语言:javascript复制
import { Button } from 'antd';
import React, { useEffect, useState } from 'react';
const IndexPage: React.FC<{ title: string }> = ({ title }) => {
  const [subTitle, setSubTitle] = useState('subTitle');

  useEffect(() => {
    console.log('初始化加载');

    return () => {
      console.log('退出卸载');
    };
  }, []);

  useEffect(() => {
    console.log(`${subTitle}渲染完成之后`);

    return () => {
      console.log(`清除上次${subTitle}渲染`);
    };
  }, [subTitle]);

  useEffect(() => {
    console.log(`${title}渲染完成之后`);

    return () => {
      console.log(`清除上次${title}渲染`);
    };
  }, [title]);

  console.log('页面刷新');

  return (
    <div style={{ border: '5px solid red', marginTop: '16px' }}>
      <div>我是子组件</div>
      <div>{title}</div>
      <div>{subTitle}</div>
      <Button type="primary" onClick={() => setSubTitle('subTitle Change')}>
        {' '}
        刷新subtitle
      </Button>
    </div>
  );
};
export default IndexPage;

组件的调用顺序

在子组件加了一些打印来观察调用顺序,我们知道useEffect 是副作用,也就是函数组件调用完成的时候去回调安排副作用。

当依赖项为空的时候,是第一次加载完成的时候调用,如果有依赖项,则依赖出发页面改变的时候去调用。

关于retrun 通常useEffect 可以写return 闭包,那么这个return 的调用时机是什么呢?

带着这些问题,我们用初次加载(useEffect 依赖为空数组)、监听属性(props)、监听state 分别进行测试。

初次加载情景

第一次加载也就是监听这部分逻辑,这个用hooks 的都知道就不多说。

代码语言:javascript复制
 useEffect(() => {
    console.log('初始化加载');

    return () => {
      console.log('退出卸载');
    };
  }, []);

项目运行打印结果:

一切在估计中,继续。

卸载应用,验证retrun 是不是卸载时候调用,我们利用父视图显示卸载子组件。

点击卸载子组件,触发打印,一切正常。

监听属性的变化

我们用useEffect 监听props 属性的变化时机,return 是什么时候调用。

代码语言:javascript复制
useEffect(() => {
    console.log(`${title}渲染完成之后`);

    return () => {
      console.log(`清除上次${title}渲染`);
    };
  }, [title]);

项目运行打印结果:

页面渲染完之后,同样执行了监听的props 。 那么props 的return 是什么时候调用呢?

刷新title 属性试试,重外面更新title 属性,结果如下:

会先执行return 把上次的props 属性卸载掉,里面的props 值还是上次的。然后再执行进入。

监听state的变化

代码语言:javascript复制
  useEffect(() => {
    console.log(`${subTitle}渲染完成之后`);

    return () => {
      console.log(`清除上次${subTitle}渲染`);
    };
  }, [subTitle]);

一如既往,useEffect 初始化的时候会这么调用。

那么return 是怎么运行的呢?我们改变state 试一试,同样会先执行return 然后再进入。

那么初始化、state 和 props 一起进入顺序如何

跑第一遍:

先执行初始化,然后state 最后props 。 真的是这样吗?换个顺序试试

换个顺序再来一遍:

wtf 顺序改变了。

结论:

进入的加载顺序和执行顺序有关,卸载之后同样。

0 人点赞