乐趣区

关于react.js:何时使用useLayoutEffect

一、对于 useLayoutEffect 的形容

Reactjs 文档里这样形容useLayoutEffect

  1. The signature is identical to useEffect, but it fires synchronously after all DOM mutations

  2. only differs in when it is fired

useLayoutEffectuseEffect函数签名统一,然而在 DOM 批改后同步触发,这是和 useEffect 惟一的区别。

二、何时应用useLayoutEffect

假如有个展现随机数字的 case,当 count 为 0 时随机生成个数字:

2.1 先应用 useEffect 实现:

import {useState, useEffect, useLayoutEffect} from 'react'

export default function App() {const [count, setCount] = useState(0);
    
    useEffect(() => {console.log(`useEffect - count=${count}`)
        // 耗时的操作
        const pre = Date.now();
        while(Date.now() - pre < 500) {}
        
        // count 为 0 时从新生成个随机数
        if (count === 0) {setCount(10 + Math.random() * 200);
        }
    }, [count]);
    
    // 点击 DIV 重置 count
    return (<div onClick={() => setCount(0)}>{count}</div>
    );
} 


能够看到展现 0 的过程。

2.2 改用 useLayoutEffect 实现:

import {useState, useEffect, useLayoutEffect} from 'react'

export default function App() {const [count, setCount] = useState(0);
    
    useLayoutEffect(() => {console.log(`useLayoutEffect - count=${count}`)
        // 耗时的操作
        const pre = Date.now();
        while(Date.now() - pre < 500) {}

        if (count === 0) {setCount(10 + Math.random() * 200);
        }
    }, [count]);
  
    return (<div onClick={() => setCount(0)}>{count}</div>
    );
}  

  1. 没有闪动,当点击 div,count 更新为 0,此时页面并不会渲染,而是期待 useLayoutEffect 外部状态批改后,才会去更新页面,所以页面不会闪动。

    Updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint

  2. 然而也能够发现页面更新的比拟卡顿,因为 useLayoutEffect 会阻塞浏览器渲染,正好本例中 useLayoutEffect 的实参函数里有个耗时操作,所以页面更新比拟卡顿。

2.3 useLayoutEffectcomponentDidMountcomponentDidUpdate 触发机会统一

下面的例子改用 class 组件实现试试:

import React from 'react'

export default class App extends React.Component {constructor(props) {super(props);
        this.state = {count: 0}
    }

    componentDidUpdate() {
        // 耗时的操作
        const pre = Date.now();       
        while(Date.now() - pre < 500) {}}

    increaseCount = () => {this.setState(({ count}) => {return { count: count + 1}
        })
    }

    render() {const { count} = this.state;
        return (<div onClick={this.increaseCount}>{count}</div>
        )
    }
}

useLayoutEffect 成果一样:也是看不到闪动,但也比拟卡顿。

2.4 综上:

  1. useLayoutEffectcomponentDidMountcomponentDidUpdate触发机会统一(都在在 DOM 批改后且浏览器渲染之前);
  2. useLayoutEffect要比 useEffect 更早的触发执行;
  3. useLayoutEffect会阻塞浏览器渲染,切记执行同步的耗时操作。

三、小结:

除非要批改 DOM 并且不让用户看到批改 DOM 的过程,才思考应用useLayoutEffect,否则该当应用useEffect

留神:如果只是为了获取 DOM 属性(或其它 get 操作),则没必要应用useLayoutEffect,该当应用useEffect

四、参考:

整顿自 gitHub 笔记 useEffectuseLayoutEffect到底有什么区别?

退出移动版