useState의 Lazy initial state

Lee Jooam·2022년 5월 5일
0

initialState의 사용

const [items, setItems] = useState(0);

위와 같은 useState 훅의 사용법은 아주 흔한 방식이다. 하지만 initialState가 다음과 같다면 어떨까?

const App = () => {
  const init = () => {
    console.log("This is my init Func");

    const items = localStorage.getItem("myItems");

    return items.split(",").map((data) => data * 2);
  };
  const [content, setContent] = useState(init());
  const inputRef = useRef();

  return (
    <div>
      <ul>
        {content.map((d, index) => (
          <li key={index}>{d}</li>
        ))}
      </ul>
      <input type='text' ref={inputRef} />

      <button
        onClick={() => {
          setContent((prev) => [...prev, Number(inputRef.current.value)]);
        }}
      >
        Submit
      </button>
    </div>
  );
};

localStorage에 접근하는 등 복잡한 연산을 initialState에 사용해야 할 때가 있다. 하지만 다음과 같은 사용은 문제를 발생시킨다.

보이는 것과 같이 리렌더링이 발생하면 initialState를 구성하는 함수가 다시 호출된다.

비용이 큰 로직이라면 이것은 굉장히 비효율적이다.

해결책

다행히 리액트는 이런 상황을 고려했는지 해결책을 마련해놨다.

  const [content, setContent] = useState(init);

간단하게 함수 호출이 아닌 함수 그 자체를 전달해주면 된다.

  const [content, setContent] = useState(() => init());

혹은 로직에 따라 이렇게 화살표 함수를 이용할 수도 있다.

  const [text, setText] = useState(() => 'hello');

그렇다면 모든 initialState를 함수로 전달하면 되지 않을까? 최초 렌더링에만 initialState를 쓰는 게 당연하니 말이다.

함수 생성도 비용임을 인지해야한다. 불필요한 함수 생성이 반복되다 보면 프로그램의 성능에 악영향을 끼칠 수 있다.

If the initial state is the result of an expensive computation, you may provide a function instead, which will be executed only on the initial render.

공식 문서에 써있듯 고비용의 연산이 포함된 경우에만 사용하도록 하자.

profile
프론트엔드 개발자로 걸어가는 중입니다.

0개의 댓글