Post

React useEffect vs useLayoutEffect

✨React에서의 render와 paint

  • render : DOM을 구성하기 위해 각 엘리먼트의 속성을 계산하는 과정
  • paint: 실제 스크린에 Layout을 표시하고 업데이트하는 과정

✨useEffect

React 공식문서 - useEffect

📘useEffect 실행 단계

https://miro.medium.com/v2/resize:fit:640/format:webp/1*Q5DfWHYDNQdfnal-IvW05g.png

component → Render → Paint → useEffect

useEffect는 컴포넌트들이 render와 paint된 후에 실행된다. 비동기적으로 DOM이 그려지고난 후에 상태 값에 따라 다시 렌더링된다.
따라서 useEffect 내부에 DOM에 영향을 주는 코드가 있다면 화면이 깜빡이게 된다.

📘문법

1
2
3
4
useEffect(() => {
  // do side effects
  return () => /* cleanup */
}, [dependancy array]);
  • 의존성 배열을 통해 어떤 상태 또는 props가 변경될 때 실행시킬지를 정함

📘useEffect 예1 - 빈 의존성 배열

현재 시간 구하기 (시간, 분, 초)

  • 1초에 한번씩 setTime()실행하여 현재 시각을 가져오는 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Time() {
  const [time, setTime] = useState(getTime());

  const getTime = () => {
    const now = new Date();
    return `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()} `;
  };

  useEffect(() => {
    const timer = setInterval(() => {
      setTime(getTime());
    }, 1000);
    return () => clearInterval(timer);
  }, []);

  return <h2>{time}</h2>;
}
  • useEffect의 의존성 배열이 비어있기 때문에 처음에 마운트 될 때만 실행
  • 컴포넌트가 언마운트될 때 return () => clearInterval(timer); 코드가 실행되어 타이머를 정지시킴

See the Pen Untitled by 혬 (@jexbagvl-the-reactor) on CodePen.

📘useEffect 예2 - 의존성 배열 활용

시작버튼을 누르면 타이머가 실행되고, 멈춤 버튼을 누르면 타이머가 종료되는 타이머

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function Timer() {
  const [running, setRunning] = useState(false);
  const [timer, setTimer] = useState(0);

  useEffect(() => {
    let timeId;
    if (running) {
      timeId = setInterval(() => {
        setTimer((prevTime) => prevTime + 1);
      }, 1000);
    }
    return () => clearInterval(timeId);
  }, [running]);
  return (
    <>
      <h2>{timer}</h2>
      <button onClick={() => setRunning(true)}>시작</button>
      <button onClick={() => setRunning(false)}>멈춤</button>
    </>
  );
}
  • useEffect의 의존성 배열에 running이 들어있기 때문에 처음 마운트 될 때와 running의 상태가 변경될 때마다 실행됨
  • running이 true일 때에는 setInterval()로직이 실행
  • running이 false일 때와 컴포넌트가 언마운트 될 때 return () => clearInterval(timeId); 코드가 실행되어 타이머가 종료됨

See the Pen Untitled by 혬 (@jexbagvl-the-reactor) on CodePen.

✨useLayoutEffect

React 공식문서 - useLayoutEffect

📘useLayoutEffect 실행 단계

https://miro.medium.com/v2/resize:fit:640/format:webp/1*ZmRLve6CMNAuaQ5EFUa-8g.png

component → Render → useLayoutEffect → Paint

useEffect와 동일하지만, 실행시점이 다르다. useLayoutEffect는 렌더링된 후 paint 전에 동기적으로 실행된다.
즉, 브라우저가 화면을 다시 그리기 전에 실행되기때문에 DOM을 조작하는 코드가 존재하더라도 사용자는 깜빡임을 보지 않는다.

애니메이션 구현같이 반응이 바로 나타나야하는 경우에 사용하는 것이 좋다.

📘문법

1
2
3
4
useLayoutEffect(() => {
  // do side effects
  return () => /* cleanup */
}, [dependancy array]);

📘useLayoutEffect 예

윈도우 리사이즈 감지

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import React, { useState, useLayoutEffect } from "react";

function WindowWidth() {
  // window.innerWidth을 초기값으로 세팅
  const [width, setWidth] = useState(window.innerWidth);

  useLayoutEffect(() => {
    const handleResize = () => {
      setWidth(window.innerWidth);
    };

    window.addEventListener("resize", handleResize);

    return () => {
      window.removeEventListener("resize", handleResize);
    };
  }, []);

  return (
    <div>
      <h2>Window Width: {width}px</h2>
    </div>
  );
}

export default WindowWidth;
  • useLayoutEffect의 의존성 배열이 비어있기 때문에 처음에 마운트 될 때만 실행
  • 컴포넌트가 언마운트될 때 return () => window.removeEventListener("resize", handleResize)가 실행되어 이벤트 리스너를 정리

See the Pen Untitled by 혬 (@jexbagvl-the-reactor) on CodePen.

결론

useEffectuseLayoutEffect는 비슷하다. “언제 실행되는지”만 다르다.

  • useEffect는 render와 paint가 모두 실행되고 난 뒤에 실행
  • useLayoutEffect는 paint가 실행되기 전에 실행

useLayoutEffect를 사용하면 성능이 저하될 수 있기때문에 useEffect를 사용해보고, 버그가 발생할 경우 useLayoutEffect를 사용하는 것이 좋다!

참고 사이트

This post is licensed under CC BY 4.0 by the author.