SUIN

useState의 지연 초기화를 통해 리액트 함수 컴포넌트의 속도를 향상시키는 방법 본문

React

useState의 지연 초기화를 통해 리액트 함수 컴포넌트의 속도를 향상시키는 방법

choi suin 2022. 12. 30. 14:52
728x90

Toast ui 포스트를 참고하던 중 리액트 지연초기화라는 것을 알게 되었다. 

원문 : https://www.benmvp.com/blog/four-characters-optimize-react-component/

 

Four characters can optimize your React component | Ben Ilegbodu

How making use of useState lazy initialization can speed up your React function component

www.benmvp.com

 

// 예제 1

const Counter = () => {
  const [count, setCount] = useState(
    Number.parseInt(window.localStorage.getItem(cacheKey)),
  )

  useEffect(() => {
    window.localStorage.setItem(cacheKey, count)
  }, [cacheKey, count])

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount((prevCount) => prevCount - 1)}>-</button>
      <button onClick={() => setCount((prevCount) => prevCount + 1)}>+</button>
    </div>
  )
}
// 예제 2

const Counter = () => {
  const [count, setCount] = useState(() =>
    Number.parseInt(window.localStorage.getItem(cacheKey)),
  )

  useEffect(() => {
    window.localStorage.setItem(cacheKey, count)
  }, [cacheKey, count])

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount((prevCount) => prevCount - 1)}>-</button>
      <button onClick={() => setCount((prevCount) => prevCount + 1)}>+</button>
    </div>
  )
}

두 코드의 차이점은 상태 초기화 부분이다. 첫 번째 예제는 localStorage에서 값을 찾아 정수로 파싱한 다음 count 상태의 초기 값으로 설정하며 두 번째 예제는 함수를 인자로 넘긴 후 첫 번째 예제처럼 초기값을 설정한다는 것이다.

 

/ 예제 1

const [count, setCount] = useState(
  Number.parseInt(window.localStorage.getItem(cacheKey)),
)
// 예제 2

const [count, setCount] = useState(() =>
  Number.parseInt(window.localStorage.getItem(cacheKey)),
)

직접적인 값이 아닌 함수를 useState 인자로 넘기는 것을 지연초기화 라고 하는데 지연초기화는 상태가 최초로 생성될 때만 실행되며 초기 상태 값 계산이 비용이 많이 드는 계산일 때 useState지연 초기화를 사용한다

이렇게 함수를 호출하여 선언해주는 것만으로도  리액트 함수 컴포넌트의 성능을 향상할 수 있다.

useState 훅은 Counter컴포넌트를 처음 렌더링할 때 count상태를 초기 값으로 생성하고 다시 setCount가 호출되면 Counter함수가 다시 호출되고 count상태는 갱신된다. 그리고 리렌더링은 count 상태가 업데이트될 때마다 발생한다. 중요한 점은 리렌더링 되는 동안, 초기 값이 다시 사용되지 않는다는 것이다.

하지만 항상 초기값을 선언할 때마다 지연 초기화 함수를 호출하는 방법은 좋지 않다 

// 원시값을 반환한다.

const Counter = () => {
  const [count, setCount] = useState(() => 0)

  // 나머지 코드
}

// 기존의 값 또는 props로 받은 값을 반환한다.

const Counter = ({ initialCount }) => {
  const [count, setCount] = useState(() => initialCount)

  // 나머지 코드
}

예제와 같은 경우 초기 값이 이미 계산된 변수이거나 원시값일 경우 함수를 한 번만 호출해도 매번 함수를 만드는 비용이 발생되며 

함수를 만드는 비용이 단순히 값이나 변수를 전달하는 것보다 더 높을 수 있기 때문에 이것은 지나친 최적화라고 한다. 


지연초기화를 사용하는 경우는 '비용이 큰 계산'을 할 때 사용하자!

-  localStorage등 에서 값을 읽는 것은 비용이 큰 계산

- 배열의. map(),. filter(),. find()등

- 현재 날짜/시간으로 초기화하는 경우 

* 만약 값을 얻기 위해 어떤 함수를 호출해야 한다면 비용이 큰 계산일 가능성이 높으며, 이러한 경우 지연 초기화를 사용하면 이득을 볼 수 있다.

예) 현재 날짜/시간으로 초기화

const Clock = () => {
  const [time, setTime] = useState(() => new Date())

  useEffect(() => {
    const intervalId = setInterval(() => {
      setTime(new Date())
    }, 1000)

    return () => {
      clearInterval(intervalId)
    }
  }, [tickAmount])

  return <p>The time is {time.toLocaleTimeString()}.</p>
}

 

 

 


정리

초기값을 구하기 위한 계산식이 존재하지 않는다면, 함수를 사용하여 선언하는 것보다 초기값을 바로 설정하는 게 좋다!

비용이 큰 계산일 경우 지연초기화를 사용하여 초기값을 선언해 리액트 함수 컴포넌트의 성능을 향상할 수 있다!!