React

useState의 초기값을 최적화해보자-초기화함수

S_sunny 2025. 5. 18. 23:55

리액트는 state의 초기값을 컴포넌트 마운트 단계에서 첫 렌더링때에만 한번 저장하고 다음 렌더링에는 무시한다.

이후 리렌더링에서는 이 초기값을 무시하고 React가 내부적으로 관리하고 있는 최신 state 값을 사용한다.

 

그래서 특정 값을 반환하는 함수로 초기값을 설정해도 리렌더링때는 그 함수의 값이 반영되지 않는다.

하지만 값이 무시될 뿐 함수의 호출은 매번 이루어지기 때문에 무의미한 계산 수행이 이루어지는 결과를 초래한다. 

function createInitialTodos() {
  const initialTodos = [];
  for (let i = 0; i < 50; i++) {
    initialTodos.push({
      id: i,
      text: 'Item ' + (i + 1)
    });
  }
  return initialTodos;
}

function TodoList() {
  const [todos, setTodos] = useState(createInitialTodos());
  // ...

 

그래서 초기화함수 또는 게으른초기화(Lazy Initialization)의 형태로 초기값을 전달하는 방법이 있다.

함수 실행 결과를 반환하는게 아니라 함수 자체를 반환하면 된다.

function TodoList() {
  const [todos, setTodos] = useState(createInitialTodos);
  // 또는 const [todos, setTodos] = useState(() => createInitialTodos());
  // ...

위처럼 함수를 useState에 전달하면 React는 초기화 중에만 그 함수를 호출하기 때문에

리렌더링 할때마다 불필요한 연산이 일어나지 않는다.

 


useEffect도 의존성 배열을 비우면 처음에만 실행할 수 있지 않은가?

 

이렇게 보니 지금까지는 처음에만 필요한 상태를 함수로 전달해야 할때 습관적으로 초기값 null 선언 후 useEffect 빈배열 에서 부여하는 방법을 사용했었는데 이 방법과 초기화 함수와의 차이도 한번 찾아보았다.

 

우선 두 방법의 가장 큰 차이는 실행시점이다.

useState 초기화 함수는 애초에 초기화에 목적이 있기 때문에 컴포넌트의 첫 렌더링 과정 중에 실행이 된다.

그래서 첫 렌더링부터 의미있는 데이터가 필요한 경우 이면서 계산 비용이 많이 드는 경우에 사용한다.

반면  useEffect는 컴포넌트가 렌더링 된 후(마운트된 후)에 실행되기 때문에 추가적인 리렌더링을 발생시키면서 값을 변경한다.

 

그렇기 때문에 계산 비용이 많이 드는 초기화 작업을 하면서 첫 렌더링부터 실제 데이터로 초기화 되어야 하는 경우에 useState의 함수초기화 방식을 사용하면 컴포넌트를 최적화할 수 있다.

로컬스토리지나 세션스토리지에서 데이터를 읽어오는 작업도 함수 초기화를 통해 최적화 할 수 있다고 한다.

실제 데이터로 시작하므로 화면 깜빡임이나 레이아웃 시프트를 줄일 수 있다는 장점이 있다.

 

하지만 컴포넌트를 렌더링하기 위해 상태값을 즉시 필요로 하기 때문에 Promise나 비동기 작업은 초기화함수로 처리할 수 없다.

그래서 API 호출과 같은 비동기 작업으로 데이터를 변경해야 할때 useEffect를 사용한다.

또한 DOM에 접근해야 하는 로직이나 컴포넌트가 완전히 마운트된 후에만 실행되어야 하는 작업은 useEffect를 사용해야 안전하게 동작한다. 이벤트 리스너 등록, 타이머 설정과 같은 사이드 이펙트가 포함된 초기화 로직은 useEffect에서 클린업 함수와 함께 처리하는것이 적합하다.

 

 

 

 

https://ko.react.dev/reference/react/useState#avoiding-recreating-the-initial-state

 

useState – React

The library for web and native user interfaces

ko.react.dev