useEffect VS useLayoutEffect
둘의 가장 큰 차이점은 useEffect 는 비동기적으로, useLayoutEffect 는 동기적으로 동작한다는 것이다.
useEffect 는 렌더링이 끝난 후 수행, useLayoutEffect 는 렌더링 전에 수행한다.
useLayoutEffect 는 렌더링 파이프라인에 영향을 줄 수 있다는 점을 고려해야 한다.
하지만, 위와 같은 현상만을 목적으로 이 글을 포스팅 하지는 않았다.
useEffect 는 업데이트 차단을 방지하기 위해 페인트 이후에 동작해야 하지만, 실제로는 페인트 후에 useEffect 가 실행된다는 보장이 없다는 것이다.
useLayoutEffect 에서 상태를 업데이트하면 동일한 렌더링의 모든 useEffect 가 페인트 전에 실행되므로 useLayoutEffect 로 전환된다.
일반적인 리액트 업데이트의 흐름이다.
1. 가상 DOM 렌더, 이펙트 스케쥴링, 실제 DOM 업데이트
2. useLayoutEffect 호출
3. 브라우저가 새 DOM 을 페인트
4. useEffect 호출
리액트 문서에는 다음과 같이 useEffect 를 설명한다.
useEffect는 브라우저가 페인팅할 때까지 지연되지만, 새로운 렌더링이 시작되기 전에 실행되도록 보장됩니다.
리액트는 항상 새 업데이트를 시작하기 전에 이전 렌더링의 이펙트를 실행(flush) 합니다.
위 뜻은 다음과 같이 해석된다.
1. 업데이트가 시작되기 전에 이펙트가 실행된다.
2. 페인트 전에 업데이트가 시작 될 수 있는 경우 페인트 전인 해당 업데이트 전에 실행된다.
이는 다음의 리액트 업데이트 흐름 예시이다.
1. 가상 DOM 렌더링, 이펙트 스케쥴링, DOM 업데이트
2. useLayoutEffect 호출
3. 상태 업데이트, 리렌더링 스케쥴링
4. useEffect 호출
5. 리액트 업데이트 2
6. 업데이트 2 로 부터 useLayoutEffect 호출
7. 새 DOM 을 페인팅
8. 업데이트 2 로 부터 useEffect 호출
그럼 어떻게 해야 할까?
- 페인트 후에 동작하는 useEffect 에 의존하지 말자.
- 사용자에게 한 프레임 동안 그려진 무언가를 보여주고 싶다면 useEffect 가 아닌
requestAnimationFrame, postMessage 트릭을 사용하자.
- useLayoutEffect 에서 상태를 업데이트 하지 말자.
- useEffect 는 상태를 업데이트하기에는 더 나쁜 곳이다. 깜빡임 현상은 나쁜 UX 를 초래하고,
UX는 성능보다 중요한 곳이다. 하지만 가끔씩 상태를 useRef 로 안전학 대체 할 수 있다.
* 참고글
useEffect sometimes fires before paint
useEffect should run after paint to prevent blocking the update. But did you know it's not really guaranteed to fire after paint? Updating state in useLayoutEffect makes every useEffect from the same render run before paint, effectively turning them into l
blog.thoughtspile.tech