[BFE.dev] useEffect() timing 4

치만·2026년 2월 1일

BFE.dev react

목록 보기
27/28
post-thumbnail

useEffect() timing 4

'infiniteLoopProtection:false'

import * as React from 'react'
import { useState, useEffect, useLayoutEffect} from 'react'
import { createRoot } from 'react-dom'

function App() {
  const [state, setState] = useState(0)
  console.log(1)
  
  const start = Date.now()
  while (Date.now() - start < 50) {
    window.timestamp = Date.now()
  }
  
  useEffect(() => {
    console.log(2)
  }, [state])

  Promise.resolve().then(() => console.log(3))

  setTimeout(() => console.log(4), 0)

  useLayoutEffect(() => {
    console.log(5)
    setState(state => state + 1)
  }, [])

  return null
}

const root = createRoot(document.getElementById('root'));
root.render(<App/>)

출력 결과

1 
5 
2 
1 
2 
3 
3 
4 
4 

풀이 과정

1. 초기 렌더링

  • 1 출력: root.render(<App />)가 실행되면서 1이 출력됩니다.

  • 5 출력: useLayoutEffect이 실행되면서 5가 출력됩니다.

  • 2 출력: useEffect이 실행되면서 2가 출력됩니다.

    🤔 useEffect가 microtask보다 먼저 실행될까?

    useLayoutEffect는 브라우저가 화면을 그리기(Paint) 직전에 실행됩니다. 이 안에서 상태를 변경(setState)하면, React는 화면을 그리지 않고, 바뀐 데이터로 다시 계산해서 그립니다.

    원래라면 나중에 실행되어야 할 다른 효과(Effects)들이 있더라도, useLayoutEffect에 의해 동기적 렌더링이 발생하면 일관성을 위해 대기 중인 작업들을 한꺼번에 처리해 버립니다.


2. 상태 업데이트 후 재렌더링

  • 1 출력: <App />이 리렌더되고 1이 출력됩니다.

  • 2 출력: useLayoutEffect에 의해 예약된 동기적 리렌더링 때문에 useEffect가 실행되면서 2가 출력됩니다.

  • 3 출력: 첫 번째 렌더링 중에 예약된 Promise 콜백이 실행되어 3을 출력합니다.

  • 3 출력: 리렌더링 중에 예약된 두 번째 Promise 콜백이 실행되어 3을 출력합니다.

  • 4 출력: 첫 번째 렌더링 중에 예약된 setTimeout 콜백이 실행되어 4를 출력합니다.

  • 4 출력: 리렌더링 중에 예약된 두 번째 setTimeout 콜백이 실행되어 4를 출력합니다.

profile
🌱개발 기록장

0개의 댓글