React에서 상태 관련 처리를 하다가...
신규 기능 생성번호 당첨내역을 개발하고 있었다.
백엔드 로직 개발을 하고, 프론트쪽 개발로 넘어왔다.
각 로또 등위별 당첨 내역 처리를 위해 아래와 같은 상태 값을 사용했다.
const [rankInfo, setRankInfo] = useState<RankSummary>({
firstRankCnt: 0,
secondRankCnt: 0,
thirdRankCnt: 0,
fourthRankCnt: 0,
fifthRankCnt: 0,
})
위 상태 값은 아래의 등위별 당첨 내역의 카운트에 쓰인다.
저 등위별 당첨 내역은 컴포넌트로 따서 구현되어 있다.
컴포넌트에서는 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 함수는 비동기임을 잘 알고 써야할 것 같다.
그나저나 백엔드, 프론트엔드 둘다 왔다갔다 하니 약간 햇갈리기도 한다.
일할때는 역시 정신 똑바로 차리고 해야 할듯...