본문 바로가기

이것저것 스터디📚/React - 공식 문서

React 공식문서 -useLayoutEffect

- useLayoutEffect는 성능을 저하시킬 수 있기 때문에, 가급적 useEffect를 사용하는 것을 권장한다.

- useLayoutEffect는 브라우저가 화면을 다시 채우기 전에 실행되는 버전의 useEffect이다.

- 즉, 브라우저가 화면을 다시 그리기 전에 useLayoutEffect를 호출하여 레이아웃을 측정한다.


Mesuring layout before the browser repaints the screen(브라우저에서 화면을 다시 그리기 전 레이아웃 측정하기)

- 예를들어 마우스오버 시 요소 옆에 툴팁을 표시하는 기능이 있다고 가정했을 때, 공간이 충분하면 툴팁이 요소 위에 표시되지만, 공간이 충분하지 않으면 아래에 표시되게 하고 싶을 때, useLayoutEffect를 사용할 수 있다.

- 이 경우 1. 툴팁을 원하는 위치에 렌더링 2. 높이를 측정하고 툴팁을 배치할 위치 결정 3. 올바른 위치에 툴팁을 다시 렌더링 과정을 거쳐야 한다.

- 이때, 위와 같은 작업을 브라우저가 화면을 다시 그리기 전에 이루어지게 하고 싶다면, 브라우저가 화면을 다시 그리기 전에 useLayoutEffect를 호출하여 레이아웃 측정을 수행할 수 있다.

function Tooltip() {
  const ref = useRef(null);
  const [tooltipHeight, setTooltipHeight] = useState(0); // You don't know real height yet
                                                         // 아직 실제 height 값을 모릅니다.

  useLayoutEffect(() => {
    const { height } = ref.current.getBoundingClientRect();
    setTooltipHeight(height); // Re-render now that you know the real height
                              // 실제 높이를 알았으니 이제 리렌더링 합니다.
  }, []);

  // ...use tooltipHeight in the rendering logic below...
  // ...아래에 작성될 렌더링 로직에 tooltipHeight를 사용합니다...
}

* useLayoutEffect VS useEffect

- useLayoutEffect는 브라우저가 다시 그리는 것을 차단하지만, useEffect는 브라우저를 차단하지 않는다.


useLayoutEffect가 서버에서 아무것도 수행하지 않습니다.

- useLayoutEffect의 목적은 컴포넌트가 렌더링에 레이아웃 정보를 사용하도록 하는 것이다.

- 사용자 또는 프레임워크가 서버 렌더링을 사용하는 경우, React 앱은 초기 렌더링을 위해 서버의 HTML로 렌더링되고 이를 통해 JavaScript 코드가 로드되기 전에 초기 HTML을 표기할 수 있다.

- 이때, 서버에는 레이아웃 정보가 없기 때문에, 위의 예제의 경우 초기 서버 HTML의 일부로 Tooltip을 렌더링하려고 하면 이를 확인할 수 없다.

- 일반적으로 레이아웃 정보에 의존하는 컴포넌트는 서버에서 렌더링할 필요가 없다. 위의 예제의 경우에도 초기 렌더링 중에 Tooltip을 표시하는 것은 의미가 없을 수 있다.(Tooltip은 클라이언트의 상호작용에 의해 촉발되기 때문)

 

* 이 문제를 해결하기 위한 몇 가지 옵션

- useLayoutEffect를 useEffect로 바꾼다. 이럴 경우에 React가 페인트를 막지 않고 초기 렌더링 결과를 표시해도 괜찮다고 알려주기 때문(Effect가 실행되기 전에 원래 HTML이 보이게 되므로) ???

- 컴포넌트를 클라이언트 전용으로 표시하기. 서버 렌더링 중에 가장 가까운 <Suspense> 경계까지의 콘텐츠를 로딩 폴백으로 대체하도록 React에게 지시할 수 있다.

- hydration 후에만 useLayoutEffect를 사용하여 컴포넌트 렌더링 하기. 서버에서 hydration이 진행되는 동안 사용자는 useLayoutEffect를 호출하지 않는 FallbackContent를 보게되고, React는 클라이언트에서만 실행되기 때문에, useLayoutEffect를 호출하는 RealContent로 대체힐수 있다.

// isMounted state 선언
const [isMounted, setIsMounted] = useState(false);

useEffect(() => {
  setIsMounted(true);
}, []);

useLayoutEffect(() => {
  // 클라이언트에서만 실행되어야 하는 로직을 작성
  // 예: DOM 조작, 애니메이션 시작 등
}, []);

- 또는 컴포넌트를 외부 데이터 저장소와 동기화하고 레이아웃 측정이 아닌 다른 이유로 useLayoutEffect에 의존하는 경우에는, useSyncExternalStore를 고려할 수 있다.


* 참고 : React 공식문서(https://react-ko.dev/learn)