티스토리 뷰

우리가 많이 사용하는 react hook 중 useState의 인자 값으로 리터럴 값 외에 함수를 넘겨줄 수도 있습니다. 이를 Lazy initial state라 하는데 자세한 내용은 맨 하단의 react 공식 문서 ref를 확인해보시면 됩니다.

🟠 Lazy initial state( 지연 초기값 )

initialState 인자는 초기 렌더링 시에 사용하는 state이다. 그 이후의 렌더링 시에는 이 값은 무시됩니다.

  • 즉, 설정해 놓은 초기값을 필요로하는 순간이 맨 처음 화면이 랜더링될 때 한번이고 입출력 계산이 필요한 경우라면 lazy init 함수를 통해 성능 최적화 작업이 가능합니다. 
  • 구체적인 예시로는 localStorage(웹스토리지) 접근, map, filter, find, new Date등 데이터 조작시 유용

초기 state가 고비용 계산의 결과라면, 초기 렌더링 시에만 실행될 함수를 대신 제공할 수 있습니다.

const [state, setState] = useState(() => {
  const initialState = someExpensiveComputation(props);
  return initialState;
});

🟠 state 갱신의 취소 

State Hook을 현재의 state와 동일한 값으로 갱신하는 경우 React는 자식을 렌더링한다거나 무엇을 실행하는 것을 회피하고 그 처리를 종료합니다. (React는 Object.is 비교 알고리즘을 사용)

 

실행을 회피하기 전에 React에서 특정 컴포넌트를 다시 렌더링하는 것이 여전히 필요할 수도 있다는 점을 주의해서 사용하자. React가 불필요하게 트리에 그 이상으로 더 깊게는 관여하지 않을 것이므로 크게 신경쓰지 않아도 되지만, 렌더링 시 고비용의 계산을 하고 있다면 useMemo를 사용하여 최적화할 수 있습니다.

 

🟠 Lazy initial state 대신 useEffect를 사용한다면 무슨 차이가 있을까?

Lazy initial state를 공부하면서 useEffect와의 차이점이 궁금하게 됐습니다. 물론 useEffect에는 의존성 배열이 있어 state가 변경되면 자동으로 내부 함수를 실행시켜준다는 차이점이 있지만 외에 초기값 세팅에서는 동일한 작업을 한다고 느껴졌기 때문입니다. 

- useEffect에 전달된 함수는 화면에 렌더링이 완료된(컴포넌트들이 렌더링 된) 이후에 수행됩니다.

  • 변형, 구독, 타이머, 로깅 또는 다른 사이드 이펙트들은 함수 컴포넌트의 본문 안에서 허용되지 않으므로 이때는 useEffect를 활용합니다. 

🟠 결론

컴포넌트가 렌더링된 이후라면, Lazy intial state가 시점이 더 빠를 것으로 예상되는데요. 제가 이해한 바로는 컴포넌트 렌더링이란 곧 컴포넌트 파일의 js를 전부 읽어서 실행시킨 것이므로 그 내부에 정의된 useState코드는 컴포넌트가 렌더링되기 이전에 실행된다는 뜻이겠지요. Loading과 같은 값을 읽어오기 전 상태를 화면에 명시적으로 표시할 때에는 useEffect를 활용하고(사용자에게 작업상태라는 것을 알려주고 싶을 때) 그럴 필요가 없다면 useState에 초기 함수를 전달해주는 것도 괜찮을 거 같습니다. 

 

🟠 예시 코드

  • useEffect로 초기값을 세팅할 때와, useState에 초기 함수를 넘겨주는 두 가지 방식으로 확인해봤습니다. 

useEffect 내부에서 setState(리터럴값)

❌useEffect 내부에서 return 하는 것은 컴포넌트가 언마운트됐을 때 실행되는 함수입니다. 이를 cleanUp함수라 하는데 실수로 return 값을 리터럴로 넣지 않도록 주의하세요. 

 

import { useState, useEffect } from "react";

export default function TestCompo() {
  useEffect(() => {
    console.log("안녕 유즈이펙트!");
    setTestState("유즈 이펙트 초기값");
  }, []);
  const [testState, setTestState] = useState();

  return <div>{testState}</div>;
}

useState(함수 초기값)

import { useState, useEffect } from "react";

export default function TestCompo() {
  
  const [testState, setTestState] = useState(() => {
    console.log("안녕!, 레이지 init!");
    return "레이지 초기값";
  });

  return <div>{testState}</div>;
}

2023-03-03 updated 

🟠 주의 사항

  • state 제공한 새 값 비교 뒤 결정된 값이 현재 값과 동일하면 React는 구성 요소와 그 자식을 다시 렌더링하는 것을 건너뜁니다(최적화). 어떤 경우에는 React가 자식을 건너뛰기 전에 여전히 구성 요소를 호출해야 할 수도 있지만 코드에 영향을 미치지 않아야 합니다.
  • React는 상태 업데이트를 일괄 처리(상태 업데이트를 처리하기 전에 이벤트 핸들러의 모든 코드가 실행될 때까지 기다린다.)합니다. 모든 이벤트 핸들러가 실행되고 해당 기능을 호출한 후 화면을 업데이트합니다. 즉, 이벤트 핸들러와 그 안의 모든 코드가 완료될 때까지 UI가 업데이트되지 않는다는 뜻입니다. 이렇게하면 단일 이벤트 중에 여러번 다시 렌더링되는 것을 방지할 수 있습니다. 단, 클릭과 같은 여러 의도적인 이벤트는 일괄처리하지 않습니다. 각 클릭은 개별적으로 처리되며 일반적으로 안전한 경우에만 일괄 처리를 수행하기 때문에 클릭 후, form이 비활성화 됐을 시에는 두번째 클릭으로 양식이 다시 제출되는 일이 없습니다. 
  • DOM에 엑세스하기 위해 React가 화면을 더 일찍 업데이트하도록 강제해야하는 경우에는 flushSync()를 활용하지만 성능 저하로 이어지기 때문에 사용을 지양해야 합니다.  

🟠 useState() : set함수

  • setState() 함수는 업데이트될 어떤 값을 넘겨줄 수도 있고, 콜백함수(업데이트 함수라고 함)를 넘겨줄 수도 있습니다. 값을 넘기게 되면 값을 교체하는 행동을 하는 것(다음 상태값 전달)입니다. 이전 상태를 기반으로 다음 상태를 계산하는 동작이 필요하다면 콜백함수로 넘겨주면 됩니다. setState함수가 연달아 있는 경우에 업데이트 함수를 넘겨주면 단순히 상태 값을 교체하는 대신 상태 값으로 무언가를 수행하도록 지시하는 것입니다. 이는 다음 렌더링 전에 동일한 상태 변수를 여러 번 업데이트할 때 사용됩니다.  
  • 이벤트 핸들러가 완료되면 React는 다시 랜더링을 트리거하는데 다시 렌더링하는 동안 React는 대기열(큐)을 처리합니다. update 함수는 렌더링 중에 실행되므로 순수함수여야만 하고 결과만 반환해야 합니다. 또한, 내부에서 상태를 설정하거나 다른 사이트이펙트를 실행하면 안됩니다. update함수 e.g.) setState((prev) => prev + 1);
  • setState() 함수가 연달아 있는 경우라면 동기적으로 처리됩니다.

 

 

ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state

 

Hooks API Reference – React

A JavaScript library for building user interfaces

reactjs.org

https://beta.reactjs.org/learn/queueing-a-series-of-state-updates

 

Queueing a Series of State Updates

A JavaScript library for building user interfaces

beta.reactjs.org

 

댓글