Pitfall | 함정
useInsertionEffect
는 CSS-in-JS 라이브러리 작성자를 위한 훅입니다. CSS-in-JS 라이브러리에서 작업 중에 스타일을 주입하고자 하는 경우가 아니라면, useEffect
나 useLayoutEffect
가 더 나을 수 있습니다.useInsertionEffect는 useEffect의 버전 중 하나로, DOM 변이 전에 실행됩니다.
useInsertionEffect(setup, dependencies?)
참조
useInsertionEffect(setup, dependencies?)
useInsertionEffect
를 호출하여 DOM 변이 전에 스타일을 주입합니다.import { useInsertionEffect } from 'react'; // Inside your CSS-in-JS library // CSS-in-JS 라이브러리 내부에서 function useCSS(rule) { useInsertionEffect(() => { // ... inject <style> tags here ... // ... <style> 태그를 여기에 주입합니다. }); return rule; }
매개변수
setup
: Effect의 로직이 포함된 함수입니다. 셋업 함수는 클린업 함수를 선택적으로 반환할 수도 있습니다. 컴포넌트가 DOM에 추가되기 전에 React는 셋업 함수를 실행합니다. 변경된 의존성으로 다시 렌더링할 때마다 React는 먼저 이전 값으로 셋업 함수를 실행한 다음(제공한 경우) 새 값으로 셋업 함수를 실행합니다. 컴포넌트가 DOM에서 제거되기 전에 React는 클린업 함수를 한 번 더 실행합니다.
- 선택적
dependencies
:setup
코드 내에서 참조된 모든 반응형 값의 목록입니다. 반응형 값에는 props, state, 컴포넌트 본문 내부에서 직접 선언된 모든 변수와 함수가 포함됩니다. linter가 React용으로 구성된 경우, 모든 반응형 값이 의존성으로 올바르게 지정되었는지 확인합니다. 의존성 목록은 일정한 수의 항목을 가져야 하며 [dep1, dep2, dep3
]와 같이 인라인으로 작성해야 합니다. React는Object.is
비교 알고리즘을 사용하여 각 의존성을 이전 값과 비교합니다. 의존성을 전혀 지정하지 않으면 컴포넌트를 다시 렌더링할 때마다 Effect가 다시 실행됩니다.
반환값
useInsertionEffect
는 undefined
를 반환합니다.주의사항
- Effect는 클라이언트에서만 실행됩니다. 서버 렌더링 중에는 실행되지 않습니다.
useInsertionEffect
내부에서는 state를 업데이트할 수 없습니다.
useInsertionEffect
가 실행될 때까지는 refs가 아직 첨부되지 않았고, DOM이 아직 업데이트되지 않았습니다.
사용법
CSS-in-JS 라이브러리에서 동적 스타일 삽입하기
기존에는 일반 CSS를 사용해 React 컴포넌트의 스타일을 지정했습니다.
// In your JS file: // JS 파일 작성 코드: <button className="success" /> // In your CSS file: // CSS 파일 작성 코드: .success { color: green; }
일부 팀은 CSS 파일을 작성하는 대신 JavsScript 코드에서 직접 스타일을 작성하는 것을 선호합니다. 이 경우 일반적으로 CSS-in-JS 라이브러리 또는 도구를 사용해야 합니다. CSS-in-JS에는 세 가지 일반적인 접근 방식이 있습니다:
- 컴파일러를 사용하여 CSS 파일로 정적 추출
- 인라인 스타일. 예:
<div style={{ opacity: 1 }}>
- 런타임에
<style>
태그 삽입
CSS-in-JS를 사용하는 경우 처음 두 가지 접근 방식(정적 스타일의 경우 CSS 파일, 동적 스타일의 경우 인라인 스타일)을 조합하여 사용하는 것이 좋습니다. 런타임 환경에서
<style>
태그 주입은 다음 두 가지 이유로 권장하지 않습니다.- 런타임 주입은 브라우저에서 스타일을 훨씬 더 자주 다시 계산하도록 합니다.
- 런타임 주입이 React 라이프사이클에서 잘못된 시점에 발생하면 속도가 매우 느려질 수 있습니다.
첫 번째 문제는 해결할 수 없지만
useInsertionEffect
를 사용하면 두 번째 문제를 해결할 수 있습니다.useInsertionEffect
를 호출하여 DOM 변경 전에 스타일을 주입합니다:// Inside your CSS-in-JS library // CSS-in-JS 라이브러리 코드 내부에 let isInserted = new Set(); function useCSS(rule) { useInsertionEffect(() => { // As explained earlier, we don't recommend runtime injection of <style> tags. // 앞서 설명한 것처럼 <style> 태그의 런타임 주입은 권장하지 않습니다. // But if you have to do it, then it's important to do in useInsertionEffect. // 하지만 런타임 주입을 해야한다면, useInsertionEffect에서 수행하는 것이 중요합니다. if (!isInserted.has(rule)) { isInserted.add(rule); document.head.appendChild(getStyleForRule(rule)); } }); return rule; } function Button() { const className = useCSS('...'); return <div className={className} />; }
useEffect
와 마찬가지로 useInsertionEffect
는 서버에서 실행되지 않습니다. 서버에서 어떤 CSS 규칙이 사용되었는지 수집해야 하는 경우 렌더링 중에 수집할 수 있습니다:let collectedRulesSet = new Set(); function useCSS(rule) { if (typeof window === 'undefined') { collectedRulesSet.add(rule); } useInsertionEffect(() => { // ... }); return rule; }
DEEP DIVE | 심층 탐구
useInsertionEffect가 렌더링 중에 스타일을 주입하거나 useLayoutEffect를 사용하는 것보다 어떤 점이 더 나은가요?
렌더링 중에 스타일을 주입하고 React가 비차단 업데이트를 처리하는 경우 브라우저는 컴포넌트 트리를 렌더링하는 동안 매 프레임마다 스타일을 다시 계산하므로 매우 느릴 수 있습니다.
useInsertionEffect
는 다른 Effect 컴포넌트에서 실행될 때 <style>
태그가 이미 주입되어 있기 때문에 useLayoutEffect
또는 useEffect
중에 스타일을 주입하는 것보다 낫습니다. 그렇지 않으면 오래된 스타일로 인해 일반적인 Effect의 레이아웃 계산이 잘못될 수 있습니다.