
'infiniteLoopProtection:false'
import * as React from "react";
import { useState, useEffect } from "react";
import { createRoot } from "react-dom/client";
import { screen, fireEvent } from "@testing-library/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)
const onClick = () => {
console.log(5)
setState(num => num + 1)
console.log(6)
}
return <div>
<button onClick={onClick}>click me</button>
</div>
}
const root = createRoot(document.getElementById('root'));
root.render(<App/>)
setTimeout(() => fireEvent.click(screen.getByText('click me')), 100)
1
3
4
2
5
6
1
2
3
4
1 출력: root.render(<App />)가 실행되면서 1이 출력됩니다.
3 출력: microtask Queue인 Promise의 then. 콜백 함수를 실행해 3이 출력됩니다.
4 출력: task Queue에 있는 setTimeout 콜백을 실행하여 4가 출력됩니다.
2 출력: useEffect가 실행되어 2가 출력됩니다.
5 출력: 클릭 이벤트가 실행되어 5가 출력됩니다.
6 출력: 다음으로 6이 출력됩니다.
1 출력: <App />이 리렌더되고 1이 출력됩니다.
2 출력: useEffect가 실행되어 2가 출력됩니다.
🤔 왜 리렌더되고 microtask보다
useEffect가 먼저 실행될까?Effect가 클릭과 같은 사용자 인터랙션에 의해 발생한 경우, React는 브라우저가 업데이트된 화면을 그리기 전에 Effect를 실행할 수 있습니다.
이는 Effect의 결과가 이벤트 시스템에서 즉시 관찰될 수 있도록 보장하기 위함이며, 대부분의 경우 이 동작은 문제없이 기대한 대로 동작합니다.
- 사용자 인터랙션으로 인해 리렌더가 발생한 경우
- layout effect 하에서 스케줄링된 경우
- React 내부적으로 여유 시간이 있다고 판단한 경우
3 출력: microtask Queue인 Promise의 then. 콜백 함수를 실행해 3이 출력됩니다.
4 출력: task Queue에 있는 setTimeout 콜백을 실행하여 4가 출력됩니다.