💻 Frontend
5. React Hook, useLayoutEffect
category
💻 Frontend
useLayoutEffect
useLayoutEffect는 성능을 저하시킬 수 있습니다. 가능한 경우 useEffect를 사용해주세요.
useLayoutEffect
는 브라우저가 화면을 다시 그리기 전에 실행되는 useEffect 버전입니다.문법
브라우저가 화면을 다시 그리기 전에
useLayoutEffect
를 호출하여 레이아웃 측정을 수행합니다.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 로직이 포함된 함수. cleanup return 함수는 선택적으로 반환할 수 있습니다. 컴포넌트가 DOM에 추가되면 React가 setup 함수를 실행합니다. 변경된 dependencies를 다시 렌더링할 때마다, React는 먼저 cleanup 함수(해당 함수를 선언한 경우)를 실행한 다음 새로운 values로 setup 함수를 실행합니다. 구성요소가 DOM에서 제거된 후, React는 cleanup 함수를 실행합니다.
dependencies
(optional): setup 코드 내에서 참조되는 모든 reactive values의 목록. Reactive values에는 컴포넌트 내에서 직접 선언된 props, state, 모든 변수와 함수가 포함됩니다. 만약 린트(ESlint)가 포함된 경우 모든 reactive value값이 dependency에 올바르게 지정되었는지 검사합니다. dependencies list에는 항목 수가 일정해야 하며[dep1, dep2, dep3]
과 같이 인라인으로 작성해야 합니다. React는 Object.is비교를 사용하여 각 dependencies를 이전 값과 비교합니다.
만약 dependencies를 생략한다면 컴포넌트가 다시 렌더링할 때마다 Effect가 다시 실행됩니다. 컴포넌트가 다시 실행되는 경우는 첫 렌더링시 이기 때문에 dependencies를 생략된 경우는 첫 렌더링 시에만 실행됩니다.
반환값
useEffect
는 undefined
를 반환합니다.주의 사항
- useLayoutEffect는 Hooks이므로 컴포넌트 또는 커스텀 hooks의 최상위 레벨에서만 호출할 수 있습니다. 루프나 조건 내에서 호출할 수 없습니다. 필요한 경우 컴포넌트를 추출하고 거기에 Effect를 옮깁니다.
- Strict Mode가 켜져있으면 React는 실제 setup 함수 전에 개발 전용 setup + cleanup 주기를 실행합니다. 이것은 cleanup 로직이 setup logic을 “반사”하고, setup이 수행하는 모든 작업을 중지하거나 취소하는지 확인하는 stress-test입니다. 이로 인해 문제가 발생한다면 cleanup 함수를 구현하세요.
- dependencies 일부가 컴포넌트 내부에 정의된 개체 또는 함수인 경우 필요 이상으로 Effect가 다시 실행될 위험이 있습니다. 이러한 문제를 해결하려면 dependencies에 불필요한 개체 및 함수를 제거하면 됩니다. Effect 외부에서 state 업데이트 및 non-reactive 로직을 호출할 수 있습니다.
- Effect는 client에서만 실행됩니다. 서버 렌더링 중에는 실행되지 않습니다.
- useLayoutEffect 내부의 코드와 예약된 모든 state 업데이트는 브라우저가 화면을 다시 그리는 것을 차단합니다. 과도하게 사용하면 앱이 느려집니다. 가능한 경우 useEffect를 선호합니다.
사용법
브라우저가 화면을 다시 그리기 전에 레이아웃 측정
대부분의 컴포넌트는 무엇을 렌더링할지 결정하기 위해 화면에서의 위치와 크기는 알 필요가 없습니다. 일부 JSX만 반환합니다. 그런 다음 브라우저는 레이아웃(위치 및 크기)을 계산하고 화면을 다시 그립니다.
때때론 이것은 충분하지 않습니다. hover의 일부 요소 옆에 나타나는 툴팁을 생각해봅시다. 공간이 충분하면 툴팁이 요소 위에 나타나야 하지만, 맞지 않으면 아래에 나타나야 합니다. 툴팁을 올바른 최종 위치에 렌더링 하라면 높이를 알아야 합니다.
이렇게 하려면 두 가지 방법으로 렌더링해야 합니다:
- 어디에서나 툴팁을 렌더링합니다.(위치가 잘못된 경우에도)
- 높이를 측정하고 툴팁을 배치할 위치를 결정합니다.
- 툴팁을 올바른 위치에 다시 렌더링 합니다.
이 모든 작업은 브라우저가 화면을 다시 그리기 전에 발생해야 합니다. 사용자가 툴팁이 움직이는 것을 보지 않기를 원합니다. 브라우저가 화면을 다시 그리기 전에 useLayoutEffect를 호출하여 레이아웃 측정을 수행합니다.
function Tooltip() { const ref = useRef(null); const [tooltipHeight, setTooltipHeight] = useState(0); // You don't know real height yet 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 = 0
으로 렌더링 됩니다(따라서 툴팁 배치가 잘못될 수도 있음).
- React는 이를 DOM에 배치하고 useLayoutEffect에서 코드를 실행합니다.
- useLayoutEffect는 툴팁의 높이를 측정하고 다시 렌더링을 트리거합니다.
- 툴팁은 실제 tooltipHeight로 다시 렌더링됩니다(따라서 툴팁이 올바르게 배치됨).
- React는 DOM에서 이를 업데이트하고 브라우저는 마침내 툴팁을 표시합니다.
useEffect를 사용한 예제와 useLayoutEffect를 사용한 예제를 비교하고 싶으시다면 공식문서에서 확인하실 수 있습니다.
React는 브라우저가 화면을 다시 그리기 전에
useLayoutEffect
내부의 코드와 그 내부에서 예약된 모든 state 업데이트가 처리되도록 보장합니다. 이렇게 하면 사용자가 첫 번째 추가 렌더링을 인지하지 않고도 툴팁을 렌더링하고, 측정하고, 다시 툴팁을 다시 렌더링할 수 있습니다. 즉, useLayoutEffect
는 브라우저가 그림을 그리는 것을 차단합니다.Note
두번의 방법으로 렌더링하고 브러우저를 차단하면 성능이 저하됩니다. 가능하면 해당 방법을 피하는게 좋습니다.