useEffect는 페인트 후에 실행된다는 보장이 없다.
useLayoutEffect에서 상태를 업데이트하면 동일한 렌더링의 모든 useEffect가 페인트 전에 실행되므로 레이아웃 이펙트로 전환된다.
💡 일반적인 리액트의 업데이트 진행
1. 리액트 작업: 가상 DOM 렌더, 이펙트 스케줄링, 실제 DOM 업데이트
2. `useLayoutEffect` 호출
3. 리액트 제어 해제, 브라우저의 DOM 페인트
4. useEffect 호출
- 일반적으로 useEffect는 레이아웃과 페인트 이후, 지연된 이벤트 중에 발생한다.
- useEffect는 새로운 렌더링이 시작되기 전에 실행되도록 보장된다.
- 때때로 useEffect가 페인트 전에 실행될 수도 있다.
a) 새 업데이트(리렌더)가 시작되기 전에 useEffect가 실행되고
b) 페인트 전에 업데이트가 시작될 수 있는 경우(ex. useLayoutEffect를 통해 트리거 되는 경우)
→ useEffect는 페인트 전인 해당 업데이트 전에 실행되어야 한다.
- 리액트 업데이트 1
→ 가상 DOM 렌더링, 이펙트 스케줄링, DOM 업데이트 - useLayoutEffect 호출
- 상태 업데이트, 리렌더링 스케줄링
- useEffect 호출
- 리액트 업데이트 2
- useLayoutEffect 호출(업데이트 2)
- 리액트 제어 해제 → 브라우저 새 DOM 페인팅
- useEffect 호출(업데이트2)
useEffect에서는 상태를 업데이트 하면 안됨
→ 상태 업데이트시 DOM 업데이트 → 깜빡임 발생
useEffect가 useLayoutEffect에 영향을 받지 않도록 보장할 수 없으므로 페인트 후 useEffect에 의존하지 마라.
- 컴포넌트에서 useLayoutEffect를 사용하지 않는다고 해도 사용자 정의 훅이 사용할수도 있음.
- 컴포넌트가 내장 리액트 훅만 사용하더라도 트리 위쪽의 useLayoutEffect 상태 업데이트는
useContext 또는 부모의 리렌더를 통해서 누수될 수 있다. - 컴포넌트가 useEffect와 memo()만 사용하더라도 업데이트의 이펙트는 전역적으로 플러시되므로
다른 컴포넌트의 사전 페인트 업데이트는 여전히 자식 이펙트를 실행한다.
TL;DR
특정 상황에서 useEffect가 페인트 이전에 실행된다.
→ 주요 원인
- useLayoutEffect에서 상태 업데이트시 페인트 전에 다시 렌더링을 요청함. → 이펙트가 렌더링 전에 실행됨
- requestAnimationFrame(RAF) 또는 마이크로태스크에서 상태 업데이트 진행
따라서
- useLayoutEffect에서 상태를 업데이트 하는 것은 앱 성능에 좋지 않다. (때로는 대안이 없을 수도 있음.)
- 페인트 후에 동작하는 useEffect에 의존하지 마라.
- useEffect에서 DOM을 업데이트하면 깜빡임이 발생할 수 있다.
(깜빡임이 없었다면 layoutEffect에서 상태 업데이트 한 것) - layoutEffect에서 상태를 설정하는 경우 성능을 위해 useLayoutEffect의 일부를 useEffect로 추출하는 것은 의미가 없다.
- 성능이 중요한 경우 useLayoutEffect에서 DOM을 수동으로 변경해라.
reference
https://velog.io/@lky5697/unintentional-layout-effect?utm_source=substack&utm_medium=email
'React' 카테고리의 다른 글
React에서의 메모이제이션 제대로 사용하기(useMemo, useCallback, memo) (0) | 2024.06.20 |
---|