0
点赞
收藏
分享

微信扫一扫

React - 13 Hooks组件之useEffect

1.useEffect

import React, { useState, useEffect } from "react";
import { Button } from 'antd';
import './Demo.less';

/*
 useEffect:在函数组件中,使用生命周期函数
   useEffect(callback):没设置依赖
     + 第一次渲染完毕后,执行callback,等价于 componentDidMount
     + 在组件每一次更新完毕后,也会执行callback,等价于 componentDidUpdate

   useEffect(callback,[]):设置了,但是无依赖
     + 只有第一次渲染完毕后,才会执行callback,每一次视图更新完毕后,callback不再执行
     + 类似于 componentDidMount

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

   useEffect(()=>{
      return ()=>{
        // 返回的小函数,会在组件释放的时候执行
        // 如果组件更新,会把上一次返回的小函数执行「可以“理解为”上一次渲染的组件释放了」
      };
   });
 */
const Demo = function Demo() {
    let [num, setNum] = useState(0),
        [x, setX] = useState(100);

    useEffect(() => {
        // 获取最新的状态值
        console.log('@1', num);
    });
    
    useEffect(() => {
        console.log('@2', num);
    }, []);

    useEffect(() => {
        console.log('@3', num);
    }, [num]);

    useEffect(() => {
        return () => {
            // 获取的是上一次的状态值
            console.log('@4', num);
        };
    }); // 这里传[num]和不传都一样

    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;

执行原理

React - 13 Hooks组件之useEffect_react

2.useEffect和useLayoutEffect的细节

①不能把useEffect放在条件判断中,可以把条件判断放在useEffect的回调函数中

②useEffect的回调函数要么不返回值,要么只能返回一个函数

③经过async修饰之后,即使不写返回,默认返回Promise实例

import React, { useState, useEffect } from "react";
import { Button } from 'antd';
import './Demo.less';

// 模拟从服务器异步获取数据
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;

3.useEffect与useLayoutEffect的区别

React - 13 Hooks组件之useEffect_react_02

useLayoutEffect先于useEffect

React - 13 Hooks组件之useEffect_react_03

两者都可以获取dom,

React - 13 Hooks组件之useEffect_react_04

import React, { useState, useEffect, useLayoutEffect } from "react";
import { Button } from 'antd';
import './Demo.less';

const Demo = function Demo() {
    // console.log('RENDER');
    let [num, setNum] = useState(0);

    /* useLayoutEffect(() => {
        if (num === 0) {
            setNum(10);
        }
    }, [num]); */

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

     视图更新的步骤:
       第一步:基于babel-preset-react-app把JSX编译为createElement格式
       第二步:把createElement执行,创建出virtualDOM
       第三步:基于root.render方法把virtualDOM变为真实DOM对象「DOM-DIFF」
         useLayoutEffect阻塞第四步操作,先去执行Effect链表中的方法「同步操作」
         useEffect第四步操作和Effect链表中的方法执行,是同时进行的「异步操作」
       第四步:浏览器渲染和绘制真实DOM对象
    */
    useLayoutEffect(() => {
        console.log('useLayoutEffect'); //第一个输出
    }, [num]);
    useEffect(() => {
        console.log('useEffect'); //第二个输出
    }, [num]);

    return <div className="demo"
        style={{
            backgroundColor: num === 0 ? 'red' : 'green'
        }}>
        <span className="num">{num}</span>
        <Button type="primary" size="small"
            onClick={() => {
                setNum(0);
            }}>
            新增
        </Button>
    </div>;
};

export default Demo;


举报

相关推荐

0 条评论