diff --git a/README.md b/README.md index 9f5dfcd..616eb87 100644 --- a/README.md +++ b/README.md @@ -46,3 +46,4 @@ | [03-1](./week-03/) | [useDebugValue](https://react.dev/reference/react/useDebugValue)
[useDeferredValue](https://react.dev/reference/react/useDeferredValue) | 2026.01.20 | | [03-2](./week-03/) | [useEffect](https://react.dev/reference/react/useEffect) | 2026.01.23 | | [04-1](./week-04/) | [useEffectEvent](https://react.dev/reference/react/useEffectEvent)
[useId](https://react.dev/reference/react/useId) | 2026.01.27 | +| [04-2](./week-04/) | [useLayoutEffect](https://react.dev/reference/react/useLayoutEffect) | 2026.01.30 | diff --git a/week-04/README.md b/week-04/README.md index 7745742..b1abcec 100644 --- a/week-04/README.md +++ b/week-04/README.md @@ -2,10 +2,11 @@ ## 발표자 -- +- 정상인 - ## 범위 - [useEffectEvent](https://react.dev/reference/react/useEffectEvent) - [useId](https://react.dev/reference/react/useId) +- [useLayoutEffect](https://react.dev/reference/react/useLayoutEffect) diff --git "a/week-04/\354\260\270\354\206\224.md" "b/week-04/\354\260\270\354\206\224.md" index 962eda7..e44ecbe 100644 --- "a/week-04/\354\260\270\354\206\224.md" +++ "b/week-04/\354\260\270\354\206\224.md" @@ -181,3 +181,53 @@ const root = hydrateRoot(domNode, reactNode, { - Single page에 둘 이상의 React app을 render 하고, React app 중 server-rendered 되는 app이 있는 경우, - **client에서 `hydrateRoot`에 `identifierPrefix`로 전달한 값과 server API의 `identifierPrefix`로 전달한 값이 같아야 함** (e.g. `renderToPipeableStream`) + +## [useLayoutEffect](https://react.dev/reference/react/useLayoutEffect) + +- Browser가 screen을 repaint 하기 이전에 Effect를 실행시켜 주는 hook +- Browser repaint 이전에 layout measurements를 수행할 때 사용 +- `useEffect`와 동일하지만 실행 시점이 다르다. + 1. `useLayoutEffect`의 Effect 실행 + 2. Browser painting + 3. Browser가 화면을 그린 뒤 `useEffect`의 Effect 실행 +- **성능에 영향을 줄 수 있으므로, 가능하면 `useEffect`를 먼저 사용한다.** + +### Reference + +- `useLayoutEffect(setup, dependencies?)` +- Parameters + - `setup` : 실행할 Effect logic + - `dependencies?` : `setup` 내부에서 사용된 reactive values +- Returns : `undefined` +- Caveats + - `setup` 함수 내부에서 실행되는 모든 코드 및 state update는 **browser에서 screen을 repainting 하는 것을 block** 하므로, 과도하게 사용하면 app이 멈출 수 있다. + - `setup` 함수 내부에서 state update를 trigger 하면 React는 **`useEffect`를 포함한 모든 Effect들을 즉시 실행**한 후 re-rendering을 진행한다. + - Browser가 화면을 그리기 전에 모든 Effect들을 정리하고 새로운 rendering cycle을 시작하기 위해 대기중인 Effect들을 처리하는 것 + +### Usage + +#### Measuring layout before the browser repaints the screen + +```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... +} +``` + +- 대부분의 component들은 JSX를 반환하기만 하고, browser가 layout을 계산해서 repaint 해 준다. +- Component가 render 되기 전에 size 등을 사용해서 layout을 계산해야 할 때 `useLayoutEffect` 활용 가능 (e.g. Tooltip 등) +- 동작 순서 + 1. `Tooltip`은 처음에 `tooltipHeight`이 0인 상태로 render 됨 + 2. React는 `Tooltip`을 DOM에 배치한 뒤 `useLayoutEffect` 코드 실행 + 3. `useLayoutEffect` 에서는 `Tooltip`의 height을 계산해서 state를 변경하고 re-render를 trigger + 4. `Tooltip`은 변경된 `tooltipHeight` 값을 사용해서 정확한 layout으로 render + 5. React는 DOM을 갱신하고, browser는 tooltip을 표시 +- 이 때, `tooltipHeight` 초깃값은 0 이지만 `useLayoutEffect` 내부에서 정확히 계산된 height으로 변경했기 때문에 height이 0인 상태의 `Tooltip`은 그려지지 않아서 화면에 보이지 않음 \ No newline at end of file