React hook의 모든 것(4) (useState - lazy initialization)

신은수·2023년 6월 4일
0

ReactJS

목록 보기
6/13

1) Lazy Initialization(게으른 초기화)이란?

Lazy Initialization은 useState를 사용하여 state를 초기화하는 과정을 lazy(게으르게)하게 실행하는 것을 의미.


2) useState(함수()) -> 코드로 보자

import React, { useEffect, useState } from "react";

function getName() {
  console.log("사실은 겁나 오래기다리는중...");
  return "은수";
}

function App() {
  const [name, setName] = useState(getName());
  const [num, setNum] = useState(0);
  return (
    <>
      <div>
        안녕하세요 {name}! 현재 숫자는{num}입니다
      </div>
      <button onClick={() => setNum((prevNum) => prevNum + 1)}>
        버튼을 클릭해봐봐
      </button>
    </>
  );
}

export default App;

컴포넌트가 매번 렌더링될 때마다 getName 함수를 실행되고 있다는 문제점이 있다.

  1. 맨 처음 렌더링 될 때, useState(getName())에서 getName()함수를 호출해서 return받은 값을 useState에 넘겨주면 해당 값으로 초기화가 일어난다.
  2. 버튼을 누르면 setNum((prevNum) => prevNum + 1)이 실행되어 리렌더링이 일어난다.
  3. useState(getName())에서 getName()함수를 호출해서 return받은 값을 useState에 넘겨준다.
  4. useState는 이미 내부적으로 초기화가 되어 있는지 확인하고, 이미 초기화되어있다면 초기화하지 않는다.

즉 리렌더링 됐을 때 실행된 getName()의 반환값을 사용하지 않는다. (의미없는 함수실행이 일어났음)


3) useState(함수) -> 코드로 보자

import React, { useEffect, useState } from "react";

function getName() {
  console.log("완전 무거운 작업");3
  return "은수";
}

function App() {
  const [name, setName] = useState(getName);
  const [num, setNum] = useState(0);
  return (
    <>
      <div>
        안녕하세요 {name}! 현재 숫자는{num}입니다
      </div>
      <button onClick={() => setNum((prevNum) => prevNum + 1)}>
        버튼을 클릭해봐봐
      </button>
    </>
  );
}

export default App;

  1. 첫 렌더링
    1-1) useState(getName)을 통해 getName함수 자체를 값으로 넘겨준다.
    -> 여기에서는 현재 getName함수 실행이 없다.
    1-2) 내부적으로 초기화가 되었는지 여부에 따라 초기화 과정을 진행한다.
    -> 현재 첫 렌더링이므로 초기화되지 않은 상태이므로 getName함수가 실행된다.

  2. 버튼을 누르면 setNum((prevNum) => prevNum + 1)이 실행되어 리렌더링이된다.
    2-1) 이미 초기화가 첫 렌더링 때 되었기 때문에 초기화 하지 않는다. 즉, 함수를 실행조차 하지 않는다.


4) 언제 Lazy Initialization을 써야할까?

a) Lazy Initialization 쓰면 좋은 예

지금 코드에서는 getName이 오래 걸리는 작업은 아니지만, getName함수가 오래 걸리는 함수였다면, Lazy Initialize로 불필요한 함수 실행을 막는 것이 굉장이 중요한 영역이 될 것이라는 것을 알게 되었다.

그렇다면 언제 Lazy Initialization을 써야할까? 문서에서는 '비싼 비용의 계산' 이 필요할 때 쓰라고 되어있다. localStorage의 접근, map, filter,find 등의 배열을 조작하는 것들이 그 예가 될 수 있다.

b) Lazy Initialization 쓰면 좋지 않은 예

반면 초기 값이 간단한 값이거나 이미 계산된 값인 경우에는 Lazy Initialization을 사용하지 않는게 좋다. 비록 함수가 게으른 초기화로 인해 한번만 호출되지만, 함수를 만드는 비용이 존재하기 때문이다. 아래 같은 예시는 과한 최적화 코드이다.

// 원시값을 리턴
const Counter = () => {
  const [count, setCount] = useState(() => 0)
}

// prop 또는 이미 존재하는 변수의 값을 리턴
const Counter = ({ initialCount }) => {
  const [count, setCount] = useState(() => initialCount)
}


정리

useState(함수())로 적으면 리렌더링될 때 마다 함수 실행하고, useState(함수)는 초기화할때만 함수 실행된다. 즉, 함수()형태는 함수를 실행시킨다음 초기화여부를 논의하고, 콜백형태는 일단 초기화할지말지 비교하고 필요할 때만 함수를 실행시킨다.

공부하며 느낀점

사실 전에 코딩하면서 useState에 함수를 리턴한적은 없었던 것 같다. 그래서 강의를 들으며 이해를 못했던 것 같기도 하고. (토큰 같은 경우도 State에 담지 않고, 그냥 쓸때마다 토큰을 localStorage에서 불러오는 방식으로 썼었는데 게으른 초기화를 사용하여 State에 담아서 코딩해야겠다는 생각이 들었다.)

출처
[리액트의 useState와 lazy initializatio]()
[Hook 시리즈] Lazy initialization 이 대체 뭔데 그래서

profile
🙌꿈꾸는 프론트엔드 개발자 신은수입니당🙌

0개의 댓글