useLayoutEffect:called after the render before browser paint

// This is a React Quiz from BFE.dev

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

import ReactDOM from 'react-dom'

function App() {

  console.log('App')

  const [state, setState] = useState(0)

  useEffect(() => {

    setState(state => state + 1)

  }, [])

  useEffect(() => {

    console.log('useEffect 1')

    return () => {

      console.log('useEffect 1 cleanup')

    }

  }, [state])

  useEffect(() => {

    console.log('useEffect 2')

    return () => {

      console.log('useEffect 2 cleanup')

    }

  }, [state])

  useLayoutEffect(() => {

    console.log('useLayoutEffect')

    return () => {

      console.log('useLayoutEffect cleanup')

    }

  }, [state])

  return null

}

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(<App/>)

"App"// Initial rendering cycle doesn't run any clean up.

"useLayoutEffect"

"useEffect 1"

"useEffect 2"


"App"// Re-render

"useLayoutEffect cleanup"// useLayoutEffect is first to be cleaned up and immediately executed.

"useLayoutEffect"

"useEffect 1 cleanup"// Regular useEffects are grouped, cleaned up and then executed for the second rendering cycle.

"useEffect 2 cleanup"

"useEffect 1"

"useEffect 2"


https://daveceddia.com/useeffect-vs-uselayouteffect/

The signature is identical to useEffect, but it fires synchronously after all DOM mutations. Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint.

Prefer the standard useEffect when possible to avoid blocking visual updates.


https://blog.logrocket.com/react-useeffect-vs-uselayouteffect-hooks-examples/

With every click of the button, the counter state is updated, the DOM mutation is printed to the screen, and the effect function is triggered. Here’s what’s really happening:

The user performs an action, i.e., clicking the button

React updates the count state variable internally

React handles the DOM mutation

The browser then paints this DOM change to the browser’s screen

The useEffect function is fired only after the browser has painted the DOM change(s)

With the click comes a state update, which in turn triggers a DOM mutation. The text contents of the h1 element have to be changed from the previous count value to the new value.

Steps 1, 2, and 3 do not show any visual changes to the user. The user will only see a change after the browser has painted the changes or mutations to the DOM. React hands over the details about the DOM mutation to the browser engine, which figures out the entire process of painting the change to the screen. The function passed to useEffect will be fired only after the DOM changes are painted on the screen. The official docs put it this way, “Even if your Effect was caused by an interaction (like a click), the browser may repaint the screen before processing the state updates inside your Effect”.

Another important thing to remember is that the useEffect function is fired asynchronously to not block the browser paint process.

Note: Although useEffect is deferred until after the browser has painted, it’s guaranteed to fire before any new renders. React will always flush a previous render’s effects before starting a new update.

useLayoutEffect可以看作是useEffect的同步版本。使用useLayoutEffect就可以达到在同一次更新流程中解绑interval的目的。

既然useLayoutEffect可以避免这个问题,那么为什么还要用useEffect呢,直接所有地方都用useLayoutEffect不就好了。

这个主要是因为useLayoutEffect是同步的,如果要在useLayoutEffect调用状态更新,或者执行一些非常耗时的计算,可能会导致React运行时间过长,阻塞浏览器的渲染,导致一些卡顿的问题。

If you replaced the useEffect Hook with useLayoutEffect, the following would happen:

-The user performs an action, i.e., clicking the button

-React updates the count state variable internally

-React handles the DOM mutation

-The useLayoutEffect function is fired

-The browser waits for useLayoutEffect to finish, and only then does it paint this DOM change to the browser’s screen

The useLayoutEffect Hook doesn’t wait for the browser to paint the DOM changes. It triggers the function right after the DOM mutations are computed. Also, keep in mind that updates scheduled inside useLayoutEffect will be flushed synchronously and will block the browser paint process.



The Difference Between useEffect and useLayoutEffect

It’s all in the timing.

useEffect runs asynchronously and after a render is painted to the screen.

So that looks like:

-You cause a render somehow (change state, or the parent re-renders)

-React renders your component (calls it)

-The screen is visually updated

-THEN useEffect runs

useLayoutEffect, on the other hand, runs synchronously after a render but before the screen is updated. That goes:

-You cause a render somehow (change state, or the parent re-renders)

-React renders your component (calls it)

-useLayoutEffect runs, and React waits for it to finish.

-The screen is visually updated


Summary

useLayoutEffect: If you need to mutate the DOM and/or do need to perform measurements

useEffect: If you don't need to interact with the DOM at all or your DOM changes are unobservable


The difference between useEffect and useLayoutEffect is in the time when the functions are invoked. To understand when the hooks are invoked it is helpful to understand that component re-render goes through the following steps. Let us assume we are implementing useEffect hook in our app.

1.User interacts with App. Let us say the user clicks a button.

2.Component state changes

3.The DOM is mutated

4.Changes are painted on the screen

5.cleanup function is invoked to clean effects from previous render if useEffect dependencies have changed.

6.useEffect hook is called after cleanup.

It should be noted that if a component is being rendered the first time, cleanup function is not invoked because there is no effect to clean up.

The difference between useEffect hook and useLayoutEffect hook is in the timing of their invocation. useEffect hook is invoked after the DOM is painted. useLayoutEffect hook on the other hand is invoked synchronously before changes are painted on the screen. The sequence of steps outlined above for useEffect implementation can be modified for useLayoutEffect as shown below:

1.User interacts with App. Let us say the user clicks a button.

2.Component state changes

3.The DOM is mutated

4.cleanup function is invoked to clean effects from previous render if useLayoutEffect dependencies have changed.

5.useLayoutEffect hook is called after cleanup.

6.Changes are painted on the screen


useEffect

The one catch is that this runs after react renders your component and ensures that your effect callback does not block browser painting. This differs from the behavior in class components where componentDidMount and componentDidUpdate run synchronously after rendering. It's more performant this way and most of the time this is what you want.

However, if your effect is mutating the DOM (via a DOM node ref) and the DOM mutation will change the appearance of the DOM node between the time that it is rendered and your effect mutates it, then you don't want to use useEffect. You'll want to use useLayoutEffect. Otherwise the user could see a flicker when your DOM mutations take effect. This is pretty much the only time you want to avoid useEffect and use useLayoutEffect instead.

-The function passed to useEffect fires after layout and paint. i.e after the render has been committed to the screen.

-This is okay for most side effects that should NOT block the browser from updating the screen.

-There are cases where you may not want the behaviour useEffect provides. e.g. if you need to make a visual change to the DOM as a side effect. To prevent the user from seeing flickers of changes, you may use useLayoutEffect.

-The function passed to useLayoutEffect will be run before the browser updates the screen.

useLayoutEffect

This runs synchronously immediately after React has performed all DOM mutations. This can be useful if you need to make DOM measurements (like getting the scroll position or other styles for an element) and then make DOM mutations or trigger a synchronous re-render by updating state.

As far as scheduling, this works the same way as componentDidMount and componentDidUpdate. Your code runs immediately after the DOM has been updated, but before the browser has had a chance to "paint" those changes (the user doesn't actually see the updates until after the browser has repainted).


When to useLayoutEffect

The right time to useLayoutEffect instead? You’ll know it when you see it. Literally ;)

If your component is flickering when state is updated – as in, it renders in a partially-ready state first and then immediately re-renders in its final state – that’s a good clue that it’s time to swap in useLayoutEffect.

This’ll be the case when your update is a 2-step (or multi-step) process. Do you want to “batch” a couple updates together before redrawing the screen? Try useLayoutEffect.

I think of useLayoutEffect as the way to squeeze in a little extra work before React updates the DOM. “Hey, you’re making some changes already – could you throw this one in there too? Awesome.”

Here’s a (contrived) example so you can see what I mean.

When you click the page*, the state changes immediately (value resets to 0), which re-renders the component, and then the effect runs – which sets the value to some random number, and re-renders again.

The result is that two renders happen in quick succession.

Try the useLayoutEffect version and then try the version with useEffect.

https://codesandbox.io/s/uselayouteffect-no-flash-ylyyg
https://codesandbox.io/s/useeffect-flash-on-render-yluoi

Notice how the version with useLayoutEffect only updates visually once even though the component rendered twice. The useEffect version, on the other hand, visually renders twice, so you see a flicker where the value is briefly 0.


Should I useEffect or useLayoutEffect?

Most of the time, useEffect is the right choice. If your code is causing flickering, switch to useLayoutEffect and see if that helps.

Because useLayoutEffect is synchronous a.k.a. blocking a.k.a. the app won’t visually update until your effect finishes running… it could cause performance issues like stuttering if you have slow code in your effect. Coupled with the fact that most effects don’t need the world to pause while they run, regular useEffect is almost always the one to use.

When do you choose useLayoutEffect over useEffect hook?

You can use useLayoutEffect hook instead of useEffect hook if your effect will mutate the DOMuseEffect hook is called after the screen is painted. Therefore mutating the DOM again immediately after the screen has been painted, will cause a flickering effect if the mutation is visible to the client.

useLayoutEffect hook on the other hand is called before the screen is painted but after the DOM has been mutated. The undesirable behavior, of mutating the DOM immediately after painting the screen, described with useEffect hook above can be avoided.

Though replacing useEffect hook with useLayoutEffect may not have a noticeable effect in simple apps, you are strongly advised against doing so (unless the situation warrants it) as stated here, to "avoid blocking visual effects". useLayoutEffect hook has its own use cases, so does useEffect hook. Read further about some use cases of useLayoutEffect at official react doc.


99% of the time, useEffect

Most of the time your effect will be synchronizing some bit of state or props with something that doesn’t need to happen IMMEDIATELY or that doesn’t affect the page visually.

Like if you’re fetching data, that’s not going to result in an immediate change.

Or if you’re setting up an event handler.

Or if you’re resetting some state when a modal dialog appears or disappears.

Most of the time, useEffect is the way to go.

One special case

One other situation you might want to use useLayoutEffect instead of useEffect is if you're updating a value (like a ref) and you want to make sure it's up-to-date before any other code runs. For example:

So in situations like that, the solution is useLayoutEffect.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,922评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,591评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,546评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,467评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,553评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,580评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,588评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,334评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,780评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,092评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,270评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,925评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,573评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,194评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,437评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,154评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容