설명을 이해하기 전 필수 개념 2가지
- Render: DOM Tree를 구성하기 위해 각 element의 스타일 속성을 계산하는 과정
- Paint: 실제 스크린에 Layout을 표시하고 업데이트하는 과정
useEffect
는 컴포넌트들이 render
와 paint
된 후 실행된다. 비동기적(asynchronous)으로 실행된다.
paint
된 후 실행되기 때문에, useEffec
t 내부에 DOM
에 영향을 주는 코드가 있을 경우 사용자 입장에서는 화면의 깜빡임을 보게된다. 이렇게 되면 불편한 사용자 경험으로 이어질 수 있다.
import { useEffect, useState } from "react";
function App() {
const [age, setAge] = useState(0);
const [name, setName] = useState("");
useEffect(() => {
setAge(25);
setName("찬민");
}, []);
return (
<>
<div className="App">{`그의 이름은 ${name} 이며, 나이는 ${age}살 입니다.`}</div>
</>
);
}
export default App;
useEffect
를 사용한 위 코드는 다음 순서대로 동작한다.
paint
effect
내부의 setNumber
, setName
호출 물론 이렇게 간단한 DOM 구조라면 금방 렌더링할 수 있겠지만, 화면이 복잡해지면 체감할 수 있을 정도로 렌더링 시간이 증가하게 된다.
useLayoutEffect
훅은 바로 이런 문제를 해결하기 위해 등장한 훅이다
useLayoutEffect
는 컴포넌트들이 render
된 후 실행되며, 그 이후에 paint
가 된다. 이 작업은 동기적(synchronous) 으로 실행된다. paint
가 되기전에 실행되기 때문에 DOM
을 조작하는 코드가 존재하더라도 사용자는 깜빡임을 경험하지 않는다.
화면에 DOM을 그리기 전에 Effect를 수행
import { useLayoutEffect, useState } from "react";
function App() {
const [age, setAge] = useState(0);
const [name, setName] = useState("");
useLayoutEffect(() => {
setAge(25);
setName("찬민");
}, []);
return (
<>
<div className="App">{`그의 이름은 ${name} 이며, 나이는 ${age}살 입니다.`}</div>
</>
);
}
export default App;
useLayoutEffect
를 사용한 위 코드는 다음 순서대로 동작한다.
useLayoutEffect
내부의 setNumber
, setName
호출
< div >그의 이름은 찬민이며, 나이는 25살 입니다.< /div> 를 paint
useLayoutEffect
는 동기적으로 실행되고 내부의 코드가 모두 실행된 후 painting
작업을 거친다. 따라서 로직이 복잡할 경우 사용자가 레이아웃을 보는데까지 시간이 오래걸린다는 단점이 있어, 기본적으로는 항상 useEffect 만을 사용하는 것을 권장한다고 한다. 구체적인 예시로는
등의 작업은 항상 useEffect
를 사용하되,
const Test = (): JSX.Element => {
const [value, setValue] = useState(0);
useLayoutEffect(() => {
if (value === 0) {
setValue(10 + Math.random() * 200);
}
}, [value]);
console.log('render', value);
return (
<button onClick={() => setValue(0)}>
value: {value}
</button>
);
};
화면이 깜빡거리는 상황일 때, 예를들어 위와같이 state
가 존재하며, 해당 state 이 조건에 따라 첫 painting
시 다르게 렌더링 되어야 할 때는 useEffect
사용 시 처음에 0이 보여지고 이후에 re-rendering 되며 화면이 깜빡거려지기 때문에 useLayoutEffect
를 사용하는 것이 바람직하다고 한다.
useEffect
의 이펙트는 DOM
이 화면에 그려진 이후에 호출된다.
useLayoutEffect
의 이펙트는 DOM
이 화면에 그려지기 전에 호출된다.
따라서 렌더링할 상태가 이펙트 내에서 초기화되어야 할 경우, 사용자 경험을 위해 useLayoutEffect
를 활용하자!