useLayoutEffect란?
useLayoutEffect 기본 형태
useLayoutEffect(setup, dependencies?)
<예시 코드>
import { useState, useRef, useLayoutEffect } from 'react';
function Tooltip() {
const ref = useRef(null);
const [tooltipHeight, setTooltipHeight] = useState(0);
useLayoutEffect(() => {
const { height } = ref.current.getBoundingClientRect();
setTooltipHeight(height);
}, []);
// ...
매개변수
setup : Effect의 로직이 포함된 함수
optional deps : setup 코드 내에서 참조된 모든 반응형 값의 목록
반환값
주의사항
Strict Mode에서의 동작
의존성 관리
사용법
브라우저에서 화면을 다시 그리기 전 레이아웃 측정하기
- 대부분의 컴포넌트는 위치와 크기를 몰라도 JSX만 반환하고 브라우저가 레이아웃을 계산한다.
- 하지만 툴팁처럼 마우스오버 시 정확한 위치에 렌더링해야 하는 경우가 있다.
- 툴팁의 올바른 위치를 결정하려면 툴팁의 높이를 알아야 한다.
- 이를 위해 두 번의 패스로 렌더링 해야 한다.
1. 툴팁을 임의의 위치에 렌더링
- 초기 위치는 잘못될 수 있음2. 툴팁의 높이를 측정하고 위치 결정
- 툴팁의 실제 높이를 측정3. 툴팁을 올바른 위치에 다시 렌더링
- 측정된 높이를 바탕으로 툴팁을 정확한 위치에 재렌더링
- 이 모든 작업은 브라우저가 화면을 다시 그리기 전에 이루어져야 한다.
- 사용자가 툴팁이 움직이는 것을 보지 않기를 원하기 때문에
- 브라우저가 화면을 다시 그리기 전에 useLayoutEffect를 호출하여 레이아웃 측정을 수행
function Tooltip() { const ref = useRef(null); const [tooltipHeight, setTooltipHeight] = useState(0); // 아직 실제 height 값을 모릅니다. useLayoutEffect(() => { const { height } = ref.current.getBoundingClientRect(); setTooltipHeight(height); // Re-render now that you know the real height // 실제 높이를 알았으니 이제 리렌더링 합니다. }, []); // ...아래에 작성될 렌더링 로직에 tooltipHeight를 사용합니다... }
- Tooltip은 초기 tooltipHeight = 0으로 렌더링됩니다
- 따라서 툴팁의 위치가 잘못 지정될 수 있음
- React는 이를 DOM에 배치하고 useLayoutEffect에서 코드를 실행
- useLayoutEffect는 툴팁 콘텐츠의 높이를 측정하고 즉시 다시 렌더링을 촉발
- Tooltip이 실제 tooltipHeight로 다시 렌더링
- 따라서 툴팁이 올바르게 배치
- React가 DOM에서 이를 업데이트하면 브라우저에 툴팁이 최종적으로 표시
- Tooltip 컴포넌트가 두 번의 패스로 렌더링되어야 하지만, (먼저 tooltipHeight를 0으로 초기화한 다음 실제 측정된 높이) 최종 결과만 볼 수 있다는 점에 유의
useLayoutEffect는 브라우저가 다시 그리는 것을 차단
useLayoutEffect를 사용하면 브라우저가 화면을 다시 그리기 전에 코드와 상태 업데이트가 처리되도록 보장한다.
- 이를 통해 툴팁을 렌더링하고, 측정한 후, 다시 렌더링할 수 있음
- 이렇게 하면 첫 번째 렌더링을 사용자가 눈치채지 못하게 됨
- useLayoutEffect는 브라우저의 페인팅을 차단
1. import 및 상태를 초기화
import { useRef, useLayoutEffect, useState } from 'react'; import { createPortal } from 'react-dom'; import TooltipContainer from './TooltipContainer.js';2. Tooltip 컴포넌트 생성
export default function Tooltip({ children, targetRect }) { const ref = useRef(null); const [tooltipHeight, setTooltipHeight] = useState(0); useLayoutEffect(() => { const { height } = ref.current.getBoundingClientRect(); setTooltipHeight(height); }, []);
- ref를 사용해 툴팁의 DOM 요소를 참조한다
- tooltipHeight 상태를 초기화한다
- useLayoutEffect로 툴팁의 높이를 측정하여 tooltipHeight 상태를 업데이트한다
3. 툴팁 위치 계산let tooltipX = 0; let tooltipY = 0; if (targetRect !== null) { tooltipX = targetRect.left; tooltipY = targetRect.top - tooltipHeight; if (tooltipY < 0) { tooltipY = targetRect.bottom; } }
- targetRect가 존재하면 툴팁의 X, Y 좌표를 계산합니다.
- 툴팁이 위쪽에 맞지 않으면 아래쪽에 배치합니다.
4. 툴팁 렌더링
return createPortal( <TooltipContainer x={tooltipX} y={tooltipY} contentRef={ref}> {children} </TooltipContainer>, document.body ); }
- createPortal을 사용하여 툴팁을 document.body에 렌더링합니다.
- TooltipContainer에 위치와 내용(자식 요소)을 전달합니다.
5. 정리
1. Tooltip 컴포넌트는 useLayoutEffect를 사용해 툴팁의 높이를 측정
2. 이를 기반으로 툴팁의 위치를 계산하여 올바른 위치에 다시 렌더링
3. 이 과정에서 첫 번째 렌더링을 사용자가 눈치채지 못하게 브라우저의 페인팅을 차단6.useLayoutEffect Vs useEffect
useLayoutEffect
- 동기적 실행: useLayoutEffect는 DOM 업데이트 직후 즉시 실행
- 이는 화면을 다시 그리기 전에 코드가 실행되어, 화면에 보이기 전에 필요한 작업들(예: 레이아웃 측정)을 처리할 수 있음을 의미
- 따라서 사용자가 화면에서 변경 사항을 볼 때까지 대기하지 않고 바로 업데이트된 내용을 보여줄 수 있음
useEffect
- 비동기적 실행: useEffect는 렌더링 후 비동기적으로 실행
- 이는 DOM 업데이트 이후에 발생하므로, 화면에 바로 반영되지 않을 수 있음
- 따라서 useEffect를 사용할 경우, 초기 렌더링 시에 UI가 제대로 조정되기 전에 사용자가 변경된 내용을 일시적으로 볼 수 있을 수 있음