[Next js] useState의 비동기

임보라·2024년 11월 16일

Next.js

목록 보기
5/23

추천관광지에서 로그인한 유저의 북마크가 있으면 체크된상태로 되야하는데
북마크체크가 되어있지않은 상태로 보여진다.

const TourismPage = () => {
  const userId = useUserId(); //유저아이디 가져오는 훅
  const { data: tourismList, isLoading } = useGetTourismListQuery(userId); //쿼리로 받아온 투어리스트 데이터

// 도시별 데이터 그룹화
  const groupedPlaces = tourismList ? groupTourismByCity(tourismList) : [];

return(
...
  {groupedPlaces.length !== 0 ? (
          Object.entries(groupedPlaces).map(([city, tourismList]) => (
            <section key={city}>
              <div className="pc-inner-width mb-[6px] flex items-center justify-between">
                <h2 className="font-semiBold text-[24px] text-[#1D1D1D]">
                  {tourismList[0]?.citytitle || '도시 정보 없음'}
                </h2>
              </div>
              <p className="pc-inner-width mb-[14px] text-sm font-normal text-[#696969]">{city}</p>

              {/* TouristSwiper로 도시별 여행지 목록을 스와이프 가능하게 표시 */}
              <TourismSwiper tourismList={tourismList} userId={userId} />
            </section>
          ))
        ) : (
          <p className="text-sm text-alert lg:mt-7 lg:flex lg:items-center lg:justify-center"></p>
        )}
)

우선 값이 제대로 들어오는지 체크

  • 투어리스트 데이터 잘가져와짐
  • 유저아이디 훅안에서 아이디값 잘 가져와짐
  • 유저아이디값 처음엔 null이었다가 가져와짐..!

Loading, Pendding상태에 따른 처리도 해주었지만 해결안됨

원인)

로그인 유저데이터를 가져오는 속도보다 투어리스트가 UI에 업데이트되는 속도가 더 빨라서 투어리스트가 업데이트된 후 로그인유저를 받아와 북마크 체크가 안되는것

useState는 비동기로 작동한다.

setState의 비동기 문제 해결 방법)

우선 로그인한상태에서의 투어리스트를 담을 state를 새로 만들고
useEffect의 의존성 배열에 유저아이디와, 기존 쿼리로 담아왔던 투어리스트를 추가하면 두 개가 업데이트 된 후 새로 만들어준 state에 값을 담아준다.

const [tourismListData, setTourismListData] = useState([]); //로그인했을때 북마크된 투어리스트상태들이 담겨

  useEffect(() => {
    if (userId && tourismList) {
      //유저아이디와 투어리스트가 있으면 tourismListData 상태에 저장
      setTourismListData(tourismList);
    }
  }, [userId, tourismList]); 

이 후 분기처리로 로그인했을때 안했을때 삼항연산자를 사용해 화면에 보여준다.
tourismListData(로그인O,북마크O) / tourismList(로그인X,북마크X)

// 도시별 데이터 그룹화 : 유저아이디 유무에 따라 할당값 다르게
const groupedPlaces = userId ? groupTourismByCity(tourismListData) : groupTourismByCity(tourismList || []);

{Object.keys(groupedPlaces).length !== 0 ? ( //groupedPlaces객체라 length사용불가, -> 객체의 키 개수를 확인하는걸로 대체
          Object.entries(groupedPlaces).map(([city, tourismList]) => (
            <section key={city}>
              <div className="pc-inner-width mb-[6px] flex items-center justify-between">
                <h2 className="font-semiBold text-[24px] text-[#1D1D1D]">
                  {tourismList[0]?.citytitle || '도시 정보 없음'}
                </h2>
              </div>
              <p className="pc-inner-width mb-[14px] text-sm font-normal text-[#696969]">{city}</p>

              {/* TouristSwiper로 도시별 여행지 목록을 스와이프 가능하게 표시 */}
              <TourismSwiper tourismList={tourismList} userId={userId} />
            </section>
          ))
        ) : (
          <p className="text-sm text-alert lg:mt-7 lg:flex lg:items-center lg:justify-center"></p>
        )}

State Batch Update

리액트는 컴포넌트의 렌더링 횟수를 최소화하기 위해서 State Batch Update를 사용한다. 이는 리액트의 이벤트 핸들러를 사용하여 여러 번의 setState를 호출할 때 이를 하나의 업데이트로 일괄 처리하여 리렌더링이 한 번만 발생하도록 하는 것이다.

예시)

const [num,setNum] = useState(0); 
const onClickCount = () => {
  setNum(num+1)
  setNum(num+1)
}
...
<button onClick={onClickCount}></button>
-> 한번 클릭했을때 일괄처리하기때문에 결과는 1만 증가한다. 

참고자료)

관련블로그

0개의 댓글