React从0开始(七):React Hooks之useEffect

534 阅读4分钟

这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战

上一篇文章我们学习了React Hooks的优点,解决了类组件的部分问题,还学习了useState的基本使用方法,在这一篇文章里我们学习一下一个新的React Hook——useEffect

一、useEffect剖析

1、 在这里我们先了解一下effect(副作用),指的是没有用于发生在数据——视图转换过程中的逻辑,比如数据请求访问DOM元素缓存操作绑定/取消事件监听订阅设置定时器等等非数据——视图逻辑中的操作,均为副作用

2、useEffect的功能:

useEffect为函数式组件提供了操作副作用的能力(原本的函数式组件内进行副作用操作是不被允许的,这样有可能会破坏数据界面一致性),useEffectReactcomponentDidMountcomponentDidUpdatecomponentWillUnmount的三个生命周期进行了整合,函数式组件可以通过useEffect钩子执行在类组件中这三个生命周期的相同逻辑

React Hooks是可以重复使用且相互独立的,因此我们在编写逻辑时,可以对每个副作用操作绑定一个useEffect钩子,这样就不需要将多个副作用操作写在一个生命周期里了

3、useEffect的使用

  • useEffect可以传入两个参数,第一个参数是一个函数,用于进行副作用操作;第二个参数则是一个数组,数组中的元素是对副作用操作执行的一个条件,当数组内元素对应的状态更新时,才进行该副作用的执行

  • 基本使用:使用useEffect:将函数作为参数传入useEffect钩子函数中,这一个函数内的逻辑则是即将进行的副作用

    function Title() {
        const [title, setTitle] = useState("Jue Jin");
        useEffect(()=>{
            document.title = `Welcome to ${title} !`;
        })
    }
    
  • 我们还可以通过限制条件指定触发副作用的状态变量:

    function Title() {
        const [title, setTitle] = useState("Jue Jin");
        const [number, setNumber] = useState(1);
        //仅在title发生变化后才执行副作用
        useEffect(()=>{
            document.title = `Welcome to ${title}`;
        },[title])
        return (
            <div>
                <h1>{number}</h1>
                <button onClick={()=>{ setNumber(number=>number+1) }}> + 1 </button>
            </div>
        )
    }
    
  • useEffect绑定后会在每次渲染后对副作用逻辑进行一次处理,如果想清除副作用,有两种方法,第一种是传入一个新的函数给useEffect,另一个则是useEffect的第二个参数传入一个空数组。当第二个参数传入的是空数组时,则说明副作用不依赖于某个状态的更新,则只会调用在组件的挂载流程(即只运行一次),不会在更新阶段进行调用

    //方法一:返回一个新函数
    //useEffect在执行副作用前,会调用上一次返回的函数
    useEffect(()=>{
        if(!destroy){
            document.title = `Welcome to ${title}`;
        }else{
            return ()=>{
                console.log("useEffect destroyed");
            }
        }
    })
    //方法二:第二个参数传入空数组
    useEffect(()=>{
        document.title = `Welcome to ${title}`;
    },[])
    
  • 我们还可以通过使用多个useEffect来将多个副作用操作分离开,不必像类组件那样麻烦地堆在同一个生命周期中

4、注意事项

其中要注意,正常的useEffect传入的函数(也就是副作用的相关操作)在每次视图的渲染后都会被调用(初次渲染+更新渲染),这就是上文所说的对componentDidMountcomponentDidUpdate的整合。

另外,componentDidMountcomponentDidUpdate中的代码是同步执行的,是会阻塞浏览器视图更新的,而useEffect中的副作用是异步的,不会阻塞浏览器进行视图更新,能够满足基本的副作用安排需求

二、useLayoutEffect

上面又说到useEffect中的副作用是异步的,不会阻塞浏览器进行视图更新,但是如果我想同步进行呢?这就可以用到useLayoutEffect了(不过一般使用useEffect就够了),下面放一张浏览器渲染的流程图:

image.png

抛开机制,使用方法与useEffect没有什么差别,其中:

  1. useEffect是在浏览器渲染完毕后才调用副作用的
  2. useLayoutEffect会在Layout(DOMCSSOM合并、排列)之后,Painting(绘制)之前执行副作用
  3. useLayoutEffect会在DOM更新后同步调用副作用
  4. 特殊用法:我们可以使用useLayoutEffect来获取DOM元素相关信息,做了相应处理以后同步触发渲染(即进行测量等操作)

三、参考文章:

《React Hooks 详解 【近 1W 字】+ 项目实战》

《30分钟精通React Hooks》