React

[React] useEffect vs useLayoutEffect

joseph0926 2024. 9. 30. 17:13

[참고: useLayoutEffect]

useLayoutEffect is a version of useEffect that fires before the browser repaints the screen.

공식문서에서 `useLayoutEffect`는 브라우저가 리페인트되기 전에 실행되는 `useEffect`라고 서술합니다

그대로 이해하면 React 컴포넌트가 브라우저에 그려지기전에 `useLayoutEffect`의 로직이 먼저 실행된다는 뜻입니다

 

이게 정확히 무슨뜻인지 이해하기 전에, 이게 의미가 있는 훅일까요?

브라우저가 리페인트되기 전 / 후가 `useEffect` 로직 실행이랑 무슨 상관일까요?

 

이를 이해하기 위해서는 리액트의 생명주기를 이해하면 좋습니다

리액트 생명주기는 아래와 같습니다

 

1. 마운트

 

- lazy 초기화 실행

  - useState등에서 lazy 초기화 함수가 실행됩니다

 

2. 업데이트

 

- 렌더링: 컴포넌트가 렌더링 / 리-렌더링됩니다

- DOM 업데이트: 리액트가 버츄얼 돔의 업데이트를 실제 돔에 반영합니다

- 브라우저 페인팅: 브라우저가 페이지를 시각적으로 표현합니다

 

3. 언마운트

 

- `useEffect`, `useLayoutEffect`를 모두 클린업합니다

 

이 생명주기에서 `useEffect`는 어떤 시점에 실행될까요?

이 링크에서 확인해보면 Effects run at the end of a commit after the screen updates. 라고합니다

즉 위의 단계에서는 브라우저 페인팅이 끝난 후가 될것입니다

 

하지만 이러면 문제가 있습니다

문제를 예시로 보겠습니다

 

const [tooltipHeight, setTooltipHeight] = useState(0);

useEffect(() => {
  const rect = ref.current?.getBoundingClientRect();
  if (!rect) return;
  const { height } = rect;
  setTooltipHeight(height);
}, []);

 

이 로직은 DOM 요소에 접근하여 높이를 알아낸 후에 그 높이를 툴팁 높이로 반영하는 로직입니다

하지만 앞서 언급했듯이 useEffect는 브라우저 페인팅 후에 효과가 발생합니다

즉, 처음 브라우저가 페인트될때 툴팁의 높이는 `0`이 됩니다, 이후 `useEffect`가 실행되어 적절한 높이가 설정될것입니다

이러면 발생하는 문제는 툴팁이 처음 위치와 이후 위치가 달라 일종의 "깜빡임" 현상이 발생할것입니다

 

이를 효과적으로 처리하기 위해서 `useLayoutEffect`를 사용할 수 있습니다

다시 `useLayoutEffect`의 정의를 보면, **리페인트 되기 전**이 이제 어떤 의미를 갖는지 더욱 알수있게되었습니다

또한 추가적으로 `useLayoutEffect`는 해당 로직이 완료되기 전까지 브라우저 페인트를 중단합니다

 

이 특성들을 보면 위의 문제를 해결하는 완벽한 솔루션이 될수있습니다

 

const [tooltipHeight, setTooltipHeight] = useState(0);

useLayEffect(() => {
  const rect = ref.current?.getBoundingClientRect();
  if (!rect) return;
  const { height } = rect;
  setTooltipHeight(height);
}, []);

 

DOM 업데이트된 `useLayoutEffect` 로직이 실행되어 툴팁의 적절한 높이를 계산할 것이고, 그것을 반영하여 브라우저가 페인팅될것입니다