MHLab blog
📜
til

23년 5월 3일 흑우집합소 개발노트 (React에서 useState의 상태 처리)

2023.05.04

thumbnail

React에서 상태 관련 처리를 하다가…

신규 기능 생성번호 당첨내역을 개발하고 있었다.
백엔드 로직 개발을 하고, 프론트쪽 개발로 넘어왔다.

각 로또 등위별 당첨 내역 처리를 위해 아래와 같은 상태 값을 사용했다.

const [rankInfo, setRankInfo] = useState<RankSummary>({
  firstRankCnt: 0,
  secondRankCnt: 0,
  thirdRankCnt: 0,
  fourthRankCnt: 0,
  fifthRankCnt: 0,
});

위 상태 값은 아래의 등위별 당첨 내역의 카운트에 쓰인다.

img01


저 등위별 당첨 내역은 컴포넌트로 따서 구현되어 있다.
컴포넌트에서는 useEffect hook을 이용해서 초기 값을 설정해준다.

useEffect(() => {
  const makeLogData = winData.lottoMakeLogData;

  setRankInfo({
    firstRankCnt: 0,
    secondRankCnt: 0,
    thirdRankCnt: 0,
    fourthRankCnt: 0,
    fifthRankCnt: 0,
  });

  makeLogData.forEach((item) => {
    if (item.rank === 5) {
      setRankInfo({
        ...rankInfo,
        ...{ fifthRankCnt: rankInfo.fifthRankCnt++ },
      });
    } else if (item.rank === 4) {
      setRankInfo({
        ...rankInfo,
        ...{ fourthRankCnt: rankInfo.fourthRankCnt++ },
      });
    } else if (item.rank === 3) {
      setRankInfo({
        ...rankInfo,
        ...{ thirdRankCnt: rankInfo.thirdRankCnt++ },
      });
    } else if (item.rank === 2) {
      setRankInfo({
        ...rankInfo,
        ...{ secondRankCnt: rankInfo.secondRankCnt++ },
      });
    } else if (item.rank === 1) {
      setRankInfo({
        ...rankInfo,
        ...{ firstRankCnt: rankInfo.firstRankCnt++ },
      });
    }
  });

  setRankInfo(updatedRankInfo);
}, [winData]);

근데 이렇게 했더니 계속 값이 증분했다.
앞에 초기화 로직을 줬지만 적용되지는 않았다.

왜 그런걸까?

이 부분을 찾아보니…
아래와 같이 정리할 수 있었다.

useState hook은 비동기로 동작하기 때문에 setRankInfo가 호출되어도 rankInfo는 즉각적으로 업데이트 되지 않는다.
그래서 이를 해결하려면 이전상태를 복사한 값을 적용함으로써 해결할 수 있다.

말이 좀 어려운데 아래 솔루션 코드를 보면 알 수 있다.

useEffect(() => {
  const makeLogData = winData.lottoMakeLogData;

  // 새로운 객체를 생성하여 상태 업데이트를 수행합니다.
  const updatedRankInfo = {
    firstRankCnt: 0,
    secondRankCnt: 0,
    thirdRankCnt: 0,
    fourthRankCnt: 0,
    fifthRankCnt: 0,
  };

  makeLogData.forEach((item) => {
    if (item.rank === 5) {
      updatedRankInfo.fifthRankCnt = updatedRankInfo.fifthRankCnt + 1;
    } else if (item.rank === 4) {
      updatedRankInfo.fourthRankCnt = updatedRankInfo.fourthRankCnt + 1;
    } else if (item.rank === 3) {
      updatedRankInfo.thirdRankCnt = updatedRankInfo.thirdRankCnt + 1;
    } else if (item.rank === 2) {
      updatedRankInfo.secondRankCnt = updatedRankInfo.secondRankCnt + 1;
    } else if (item.rank === 1) {
      updatedRankInfo.firstRankCnt = updatedRankInfo.firstRankCnt + 1;
    }
  });

  setRankInfo(updatedRankInfo);
}, [winData]);

위와 같이 새로운 객체를 주고 해당 값으로 증가를 해준 다음 해당 값으로 setRankInfo를 보내주면 해결이 된다.


정리

useState, useEffect 함수는 비동기임을 잘 알고 써야할 것 같다.
그나저나 백엔드, 프론트엔드 둘다 왔다갔다 하니 약간 햇갈리기도 한다.

일할때는 역시 정신 똑바로 차리고 해야 할듯…


작은 개인광고 양해 바랍니다 ^^;;
👇 주인장이 직접 만든 서비스 👇
/static/29a05fefb322c94d5eb3f7d05c7c224e/myc_icon.png
Typescript
React
Next.Js
Nest.Js
마와셀(웹) - 와인 가격 비교
와인 가격 비교 서비스
postweb
/static/29a05fefb322c94d5eb3f7d05c7c224e/myc_icon.png
Dart
Flutter
hive
provider
마와셀(엡) - 와인과 셀러 관리, 시음노트
보유한 와인의 관리, 시음노트 작성, 보유 와인 셀러의 관리 어플리케이션
/static/d35d260fd4813f4a6d284a7f4fbcdf49/bcow_icon.png
Typescript
React
Next.Js
Nest.Js
흑우집합소(웹) - 로또번호 추천 서비스
로또번호 추천 서비스
/static/d35d260fd4813f4a6d284a7f4fbcdf49/bcow_icon.png
Dart
Flutter
drift
provider
흑우집합소(앱) - 로또번호 추천 서비스
로또번호 추천 서비스

© Powered by danmin