useState와 그 대체재 useRef
본문 바로가기

Frontend/React

useState와 그 대체재 useRef

useState에서 변경한 값이 바로변경되지 않는 이유

React에서 useState의 상태 업데이트는 비동기로 처리된다.

따라서 리렌더링을 완료하지않은경우 이전값을 가져오는 경우가 발생할 수 있어 주의가 필요하다.

 

반면 useRef는 값이 바뀌더라도 리렌더링과 관계없이 바로 참조할 수 있는 최신 값을 유지할 수 있다. 단순한 값의 저장 및 참조만 필요한 상황에서는 useRef를 고려하면좋다.

 

useState를 사용하면서 상태 업데이트의 비동기 특성까지 고려하고싶다면, useEffect나 콜백함수 내부에서 상태를 참조하는 방식으로 수정 할 수있다. 의존성으로 추가하여 상태 변화 시 최신값을 참조하도록 한다.

 

// useRef 대신 useState 사용
const [listCount, setListCount] = useState(0);

useEffect(() => {
  if (dataCsIntegration) {
    let newRowData = [];
    newRowData = dataCsIntegration.items.map((item) => {
      ...
    setListCount(dataCsIntegration.totalElements); // listCount 업데이트
    
  }
}, [dataCsIntegration]);

const fetchAll = useCallback(async () => {
  try {
    // listCount를 함수 내부에서 참조
    if (listCount > 1000) {
      throw new Error('1000개 이상은 조회할 수 없습니다.');
    }

    const { data } = await apiClient.get(`...`, {
      params: { ...searchOption, size: listCount } // 최신 listCount 사용
    });
...
  } catch (error) {
    enqueueSnackbar(`${error.message}`, { variant: 'error' });
    throw new Error(error);
  }
}, [listCount]); // listCount 의존성 추가

 

fetchAll함수에서 listCount를 바로 참조하지 않고 useCallback에 listCount를 의존성으로 추가하여 최신값을 사용할 수 있도록하였다.

그러나 사실 해당 예제의 경우 listCount가 UI리렌더링과 관련없이 단순히 최신 값을 저장하고 사용하는 용도로만 쓰이고있기때문에 useRef를 사용하여 성능최적화를 유지(불필요한 리랜더링방지)하면서도 필요한 곳에 최신값을 바로 참조할 수 있다.

 

// useRef 대신 useState 사용
const listCount = useRef(0);

useEffect(() => {
  if (dataCsIntegration) {
    let newRowData = [];
    newRowData = dataCsIntegration.items.map((item) => {
      ...
    listCount.current = dataCsIntegration.totalElements; // listCount 업데이트
    
  }
}, [dataCsIntegration]);

const fetchAll = useCallback(async () => {
  try {
    // listCount를 함수 내부에서 참조
    if (listCount.current > 1000) {
      throw new Error('1000개 이상은 조회할 수 없습니다.');
    }

    const { data } = await apiClient.get(`...`, {
      params: { ...searchOption, size: listCount.current } // 최신 listCount 사용
    });
...
  } catch (error) {
    enqueueSnackbar(`${error.message}`, { variant: 'error' });
    throw new Error(error);
  }
}, []);

 

useRef를 사용할 때 주의할 점

useRef를 사용할 때 의존성배열에는 추가하지 않는 것이 좋다. 이미 최신 상태를 유지하기때문에 의존성배열에 추가하지않아도 그 값을 바로사용할 수 있고 의존성 배열에 추가할 경우 불필요한 리랜더링을 초래할 수 있다.

반응형