React Hooks ----useEffect /useLayoutEffect了解及使用

307 阅读4分钟

useEffect 是为了让函数组件有和类组件一样的生命周期函数,可以在函数组件内通过useEffect实现接口调用、数据加载等操作。

useEffect(callback[,[]])

参数:
第一个参数是要执行的函数
第二个参数是一个依赖项数组数组(根据需求第二个参数可选是否填写),根据数组里的变量是否变化决定是否执行函数

在useEffect内部需要的值,可以通过第二个参数传入,可传递多个,用于请求数据或者加载页面

用法

useEffect:在函数组件中,使用生命周期函数

useEffect(callback):没设置依赖

  • 第一次渲染完毕后,执行callback,等价于 componentDidMount
  • 在组件每一次更新完毕后,也会执行callback,等价于 componentDidUpdate

useEffect(callback,[]):设置了,但是无依赖

  • 只有第一次渲染完毕后,才会执行callback,每一次视图更新完毕后,callback不再执行
  • 类似于 componentDidMount

useEffect(callback,[依赖的状态(多个状态)]):

  • 第一次渲染完毕会执行callback
  • 当依赖的状态值(或者多个依赖状态中的一个)发生改变,也会触发callback执行
  • 但是依赖的状态如果没有变化,在组件更新的时候,callback是不会执行的

函数写法

useEffect(()=>{
    return ()=>{
        // 返回的小函数,会在组件释放的时候执行
        // 如果组件更新,会把上一次返回的小函数执行「可以“理解为”上一次渲染的组件释放了」
    };
});

函数操作,是先添加到effect链表,然后在第二次渲染时运行内部操作

useEffect原理

useEffect运行,是在当前视图更新完毕【产生真实DOM】之后,通知callback去执行effect链表

有一个effect链表,在组件渲染的时候,方法执行时,执行组件内部的useEffect 的callback,通过mountEffect的方法将useEffect 的callback放到effect链表当中
等到组件更新完,会通过updateEffect方法通知链表中指定的callback执行的过程

注意点⚠️

1.useEffect必须在函数的最外层上下文中调用,不能把其嵌入到条件判断、循环等操作语句中

图片.png

2.异步获取数据(使用promise / async await 等用法)

图片.png

例子:

import React, { useState, useEffect } from "react";
import { Button } from 'antd';
 
// 模拟从服务器异步获取数据
const queryData = () => {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve([10, 20, 30]);
        }, 1000);
    });
};
 
const Demo = function Demo() {
    let [num, setNum] = useState(0);
 
    /* // useEffect必须在函数的最外层上下文中调用,不能把其嵌入到条件判断、循环等操作语句中
    if (num > 5) {
        useEffect(() => {
            console.log('OK');
        });
    } */
    useEffect(() => {
        if (num > 5) {
            console.log('OK');
        }
    }, [num]);
 
    // 第一次渲染完毕后,从服务器异步获取数据
    /* // useEffect如果设置返回值,则返回值必须是一个函数「代表组件销毁时触发」;下面案例中,callback经过async的修饰,返回的是一个promise实例,不符合要求!!
    useEffect(async () => {
        let data = await queryData();
        console.log('成功:', data);
    }, []); */
 
    /* useEffect(() => {
        queryData()
            .then(data => {
                console.log('成功:', data);
            });
    }, []); */
 
    useEffect(() => {
        const next = async () => {
            let data = await queryData();
            console.log('成功:', data);
        };
        next();
    }, []);
 
    const handle = () => {
        setNum(num + 1);
    };
    return <div className="demo">
        <span className="num">{num}</span>
        <Button type="primary"
            size="small"
            onClick={handle}>
            新增
        </Button>
    </div>;
};
 
export default Demo;

useLayoutEffect 与 # useEffect 二者区别

二者用法一致 但渲染时机不同

useLayoutEffect的渲染位置在虚拟节点渲染 中的  render方法(需要将DOM对象创建出来)之后,然后还没有渲染节点,在浏览器绘制和渲染之前

useEffect是在浏览器绘制和渲染之后

渲染时机的不同,为了防止阻塞渲染 尽可能使用 useEffect

useLayoutEffect会阻塞浏览器渲染真实DOM,优先执行Effect链表中的callback;

useEffect不会阻塞浏览器渲染真实DOM,在渲染真实DOM的同时,去执行Effect链表中的callback;

  • useLayoutEffect设置的callback要优先于useEffect去执行!!
  • 在两者设置的callback中,依然可以获取DOM元素「原因:真实DOM对象已经创建了,区别只是浏览器是否渲染」
  • 如果在callback函数中又修改了状态值「视图又要更新」
  • useEffect:浏览器肯定是把第一次的真实已经绘制了,再去渲染第二次真实DOM
  • useLayoutEffect:浏览器是把两次真实DOM的渲染,合并在一起渲染的

如果链表中的callback执行又修改了状态值「视圈更新」 对于 useEffect来讲:第一次真实DOM已经渲染,组件更新 会重新渲染真实的DOM; 所以频繁切换的时候,会出现样式/内容闪烁!!

对于 useLayoutEffect 来讲:第一次真实DOM还未渲染,遇到callback中修改了状态,视图立即更新,创建出新的virtualDOM,然后和上一次的virtuaIDOM合并在一起渲染为真实DOM;也就是 此类需求下,真实DOM只渲染一次,不会出现内容/样式的闪烁,