Frameworks, Platforms and Libraries/React

useEffect vs useLayoutEffect

iam102 2023. 8. 6. 17:22

Vue.js legacy 코드를 React로 porting 하면서 기존에 사용 중이던 video.js에서 warning이 뜨는 현상이 발견되었습니다.

The YouTube player is not attached to the DOM. API calls should be made after the onReady event. 

디버깅을 하다 보니 video.js를 Cleanup 하는 상황에서 해당 warning이 발생하였습니다.

...

const videoPlayer = useRef<VideoJsPlayer | null>(null);

useEffect(() => {
  videoPlayer.current = videojs(video.current!, options());

  return () => {
    if (videoPlayer.current) {
      // Warning Point!!!
      videoPlayer.current.dispose();
    }
  };
}, []);

...

video.js를 통해 만든 video player의 초기화 시점의 문제로 컴포넌트가 화면에 paint 되기 전에 초기화하도록 방법을 찾아야 했습니다.

 

useLayoutEffect?

컴포넌트가 화면에 paint 되기 전에 Event가 실행할 수 있는 hook useLayoutEffect를 찾게 되었습니다.

React 공식 문서에선 useLayoutEffect를 다음과 같이 설명하고 있습니다.

이 함수의 시그니처는 useEffect와 동일하긴 한데, 모든 DOM 변경 후에 동기적으로 발생합니다. 이것은 DOM에서 레이아웃을 읽고 동기적으로 리렌더링하는 경우에 사용하세요. 
useLayoutEffect의 내부에 예정된 갱신은 브라우저가 화면을 그리기 이전 시점에 동기적으로 수행될 것입니다.

 

useEffect와 결은 같지만 화면을 paint 하는 이전 시점에 수행되며 동기적으로 수행된다고 설명되어있습니다. 이 점은 Flow Diagram을 보면 더 명확히 보입니다.

 

출처: hook-flow(https://github.com/donavon/hook-flow)

useLayoutEffect의 경우 useEffect와 달리 컴포넌트가 paint 되기 전에 실행되며 두 hook의 가장 큰 차이점은 실행되는 시점의 차이임을 알 수 있습니다.

useEffect을 사용하면서 데이터에 따라 UI를 그리는 케이스에서 화면이 깜빡이는 현상(Flickr)은 useEffect는 컴포넌트의 paint가 끝난 후에 실행이 되기 때문입니다. 그런 상황에 useLayoutEffect을 사용하면 해당 현상(Flickr)이 발생하지 않습니다.

 

...

const videoPlayer = useRef<VideoJsPlayer | null>(null);

useLayoutEffect(() => {
  videoPlayer.current = videojs(video.current!, options());

  return () => {
    if (videoPlayer.current) {
      // Warning Point!!!
      videoPlayer.current.dispose();
    }
  };
}, []);

...

따라서 hook을 useLayoutEffect로 바꾸면 video.js에서 warning이 뜨지 않습니다.

 

useEffect vs useLayoutEffect

useLayoutEffect은 렌더링 후 컴포넌트가 paint 되기 전에 동기적으로 실행이되는 반면 useEffect는 렌더링 후 컴포넌트가 paint 된 이후에 비동기적으로 실행됩니다.

 

다만 React 공식 문서를 다시 보면 useLayoutEffect보다 useEffect를 사용하도록 권고합니다. 

화면 갱신 차단의 방지가 가능할 때 표준 useEffect를 먼저 사용하세요.

 

이유는 useLayoutEffect의 특징인 동기적으로 수행을 한 후 paint 한다는 점입니다. 아무런 문제가 없어 보이지만 useLayoutEffect을 통해 paint 이전에 동기적으로 수행되는 로직 때문에 자칫 사용자가 실제 화면으로 보는 데까지 시간이 오래 걸릴 수 있습니다. 

 

어떻게 사용해야 할까?

useLayoutEffect의 경우 Flickr같은 현상을 해결하거나 컴포넌트가 paint 되기 전 실행되어야 할 event가 있는 특수한 경우에 사용하고 기본적으로는 useEffect를 사용하도록 권장합니다. 

 


참고 자료

https://ko.reactjs.org/docs/hooks-reference.html#uselayouteffect

https://pubudu2013101.medium.com/what-is-the-real-difference-between-react-useeffect-and-uselayouteffect-51723096dc19