diff --git "a/week-04/\354\247\204\355\230\201.md" "b/week-04/\354\247\204\355\230\201.md" index a4d3fe4..375fa2b 100644 --- "a/week-04/\354\247\204\355\230\201.md" +++ "b/week-04/\354\247\204\355\230\201.md" @@ -199,3 +199,116 @@ const root = hydrateRoot(domNode, reactNode, { identifierPrefix: 'react-app1', }); ``` + +# useLayoutEffect +useLayoutEffect는 브라우저가 화면을 그리기 전에 실행되는 useEffect의 다른 버전 +- useEffect는 성능 저하가 생길수있으니 되도록 useEffect를 사용 +```javascript +useLayoutEffect(setup, dependencies?) +``` + +## Reference +브라우저가 화면을 다시 그리기 전에 레이아웃 측정이 필요할 때 useLayoutEffect 호출 +```javascript +function Tooltip() { + const ref = useRef(null); + const [tooltipHeight, setTooltipHeight] = useState(0); + + useLayoutEffect(() => { + const { height } = ref.current.getBoundingClientRect(); + setTooltipHeight(height); + }, []); +``` + +## Parameters +- setup : + - Effect 로직 함수 + - 선택적으로 cleanup 함수를 반환 + - 컴포넌트가 커밋되기 전에 실행 + - 의존성이 변경되면 이전값으로 cleanup -> 새 값으로 setup + - 컴포넌트가 DOM에서 제거되기 전에 cleanup 실행 +- optional dependencies? : + - setup 에서 사용하는 모든 반응형값의 리스트 + - object.is로 이전 값과 비교 + - 생략시 컴포넌트가 커밋될때마다 실행 + +## Returns +undefined 반환 + +## Caveats +- 컴포넌트 최상위에서 호출 +- Strict Mode 에서는 첫 setup 실행전에 개발환경에서 setup -> cleanup을 한번 실행 +- 의존성에 객체나 함수가 있으면 불필요한 재실행 위험이 있음 +- Effect는 클라이언트에서만 실행 +- useLayoutEffect 안의 코드, state 업데이트는 브라우저 화면을 그리는걸 차단 + - 과도하게 사용시 성능 저하 + - 가능하면 useEffect를 사용 +- useLayoutEffect 안에서 state를 업데이트하면 남아있는 모든 Effect를 즉시 실행 + +## Usage +### 브라우저가 그리기 전에 레이아웃 측정하기 +대부분 컴포넌트는 : +1. JSX 반환 +2. 브라우저가 위치/크기 계산 +3. 화면 그리기 + +툴팁처럼 위치를 결정하려면 실제 크기를 알아야 하는 경우 +-> 위에 공간이 없으면 아래 등 다른 위치에 표시해야 하는 경우 + +2-pass 렌더링 : +1. 툴팁을 임시 위치로 렌더링 +2. 실제 높이 측정, 툴팁의 위치 결정 +3. 툴팁을 올바른 위치로 다시 렌더링 +모든 과정이 브라우저가 화면을 그리기 전에 실행되어야함 +-> useLayEffect 사용 + +```javascript +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... +} +``` +1. tooptipHeight = 0 상태로 렌더링 +2. DOM에 반영, useLayoutEffect 실행 +3. useEffect에서 height 측정 -> 즉시 리렌더링 +4. 툴팁이 올바른 위치에 렌더링 +5. React가 DOM을 업데이트, 브라우저가 툴팁을 화면에 보여줌 +- 툴팁은 2 passes 로 렌더링 ( 임시값 0, 실제 계산된 값) +- 사용자는 최종 결과만 확인 + +### useLayoutEffect vs useEffect +useLayoutEffect +- 브라우저 그리기 (repaint) 차단 +- 레이아웃 측정 -> 즉시 반영 +- 성능 부담 큼 +- +useEffect +- 브라우저 그리기 (repaint) 이후 실행 +- 중간 상태가 화면에 나타남 +- 성능 부담 적음 + +## Troubleshooting +“useLayoutEffect does nothing on the server” 에러 +- 서버에는 레이아웃 정보가 없음 +- 서버에서 DOM 크기/위치 계산 불가 +- 대체적으로 레이아웃 정보가 필요한 컴포넌트는 서버에서 렌더링 할 필요 없음 (ex. 툴팁) + +### 해결 방법 +- useLayoutEffet 대신 useEffect 사용 + - 초기 렌더링 결과를 보여주게됨 +- 컴포넌트를 클라이언트 전용으로 처리 + - 서버 렌더링중에 가까운 의 fallback으로 처리 +- useLayoutEffect를 사용한는 컴포넌트를 hydration 이후에 렌더링 + - isMounted 를 state로 활용 + - hydration 중에는 useLayoutEffect를 사용하지 않는 FallbackContent + - hydration 이후에 RealContent 렌더링 +- 외부 데이터에 동기화 목적일 때 + - useSyncExternalStore 사용 고려 + - SSR 지원