오늘도 BEF 의 렌더링 문제를 풀면서 배운것을 정리해본다.
오늘은 사이드 이팩트 여러개를 정리해본다
오늘 문제는 아래와 같다
import * as React from 'react'
import { useState, useEffect, useLayoutEffect, useInsertionEffect} from 'react'
import { createRoot } from 'react-dom/client'
function App() {
console.log(1)
const [state, setState] = useState(0)
useEffect(() => {
setState(state => state + 1)
}, [])
useEffect(() => {
console.log(2)
return () => {
console.log(3)
}
}, [state])
useEffect(() => {
console.log(4)
return () => {
console.log(5)
}
}, [state])
useLayoutEffect(() => {
console.log(6)
return () => {
console.log(7)
}
}, [state])
useInsertionEffect(() => {
console.log(8)
return () => {
console.log(9)
}
}, [state])
console.log(10)
return null
}
const root = createRoot(document.getElementById('root'));
root.render(<App/>)
와 정말 복잡하다..
이 문제에서는 useEffect 와 useLayoutEffect , useInsertionEffect 가 등장한다.
useEffect와 useLayoutEffect 는 알고 있었는데
useInsertionEffect는 생소하다..
다시 따로 정리를 해본다
리액트 18에서 추가된 훅으로, "DOM 변경 전"에 실행되는 유일한 이펙트입니다.
"DOM 변경 직후, 브라우저가 화면을 그리기 전(Paint 전)에 동기적으로 실행되는 이펙트"입니다.
즉, useInsertionEffect 와 useLayoutEffect 는 디자인을 수정할때 사용하게 된다
그렇다면 실행 순서를 정리해보자
리액트는 렌더 단계(Render Phase)와 커밋 단계(Commit Phase)가 나뉘어져 있다
리액트가 계산하는 단계 이다
이 단계에서는 실제 DOM을 건드리지 않습니다. 그냥 가상 DOM(Virtual DOM)을 만들고, 이전 것과 비교해서 "어디를 바꿔야겠네?"라고 계획만 세우는 단계이다.
따라서 콘솔로그는 찍히지만 , 아직화면이나 실제 DOM은 바뀌지 않는 단계
"실제 DOM에 적용하는 단계"
리액트가 렌더 단계에서 세운 계획을 바탕으로 실제 브라우저의 DOM 노드를 생성, 수정, 삭제하게 된다
useInsertionEffect와 useLayoutEffect는 바로 이 Commit Phase 도중에 실행되게 된다

useEffect 에서 return을 쓰면 정리가 된다.
나는 처음 정리 타이밍은 useEffect가 실행되기 이전에 정리가 실행되는줄알고있었다. 다만 useEffect훅스가 여러개 일때 하나씩 정리되고 useEffect가 실행되는줄 알았는데 거기서 오해가 있었다.
useEffect 정리 타이밍에서 정리는 모두 모여서 실행이 된다.
왜 이렇게 되는가 싶어서 찾아봤더니 이러한 이유가 있었다
중간 상태의 불일치: 1번 이펙트가 실행되면서 상태를 건드렸는데, 아직 2번 이펙트는 정리조차 안 된 '과거 상태'에 머물러 있다면 로직이 꼬일 수 있습니다.
브라우저 성능: 이펙트 하나 실행할 때마다 브라우저가 레이아웃을 다시 계산해야 할 수도 있는데, 이걸 모아서 처리하면 브라우저가 훨씬 효율적으로 화면을 업데이트할 수 있습니다.
이정도 배웠으니 이제 문제를 풀수 있을 것이다. 다시한번 문제를 보고 풀어보도록 하자! 오늘도 새로운 것을 알고 간다