깊고 넓은 삽질

react query에서 mutate를 기다리지 않고 onSuccess콜백이 실행되는 것 같을 때 본문

Programming

react query에서 mutate를 기다리지 않고 onSuccess콜백이 실행되는 것 같을 때

ggsno 2024. 2. 29. 12:05

리액트 쿼리는 mutate 함수의 옵션 값으로 onSuccess 콜백을 설정할 수 있다. mutate가 성공했을 때 호출되는 콜백함수다. 나는 주로 mutate 성공 시 mutate와 연관되는 쿼리들의 쿼리키들을 invalidate하게 해서 데이터를 동기화할 때 사용한다.

 

그런데 가끔 mutate를 기다리지 않고 onSuccess를 호출하는 것 같은 때가 있다.

 

아래의 코드는 찜하기(favorite) 훅의 구현이다.

찜하기 버튼

어딘가 잘못된 코드

...

const { data: favorites, ...rest } = useSuspenseQuery({
    queryKey: [queryKey.favorite],
    queryFn: async () => {
      const res = await apiInstance.get<FavoriteProps[]>(`/favorites`);
      return res.data;
    },
});

const isFavorite = favorites.some((e) => e.productId === productId);

const addFavorite = useMutation({
    mutationFn: async (productId: ProductProps["id"]) => {
      apiInstance.post(`/favorites`, { productId });
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: [queryKey.favorite] });
    },
}).mutateAsync;

...

favorites 객체 배열

favorites는 찜을 한 유저 아이디와 상품 아이디로 구성된 객체의 배열이다. API GET /favorites를 호출하면 로그인한 유저의 favorites를 받아올 수 있다.

isFavorite 변수

상품을 찜의 여부에 따라 다른 형태로 보여줘야하기 때문에 isFavorite이라는 변수가 있다. 내가 찜한 상품 목록들을 순회하면서 현제 선택된 productId가 있는지 여부에 따라 boolean 값을 가진다.

addFavorite 함수

위의 favorite 쿼리에 관련된 mutate함수다. 이 함수가 호출되고 성공 시 바뀐 최신의 favorites를 받아오기 위해 favorites 쿼리를 invalidate한다.

비정상적인 isFavorite

위의 코드를 실행하면 isFavorite 변수가 좀 이상할 것이다. 위의 코드는 addFavorite 함수가 성공하는 것을 기다리지 않고 favorites 쿼리를 재호출하는 것처럼 작동 할 것이다. isFavorite은 favorites 쿼리 재호출 시에는 아직 addFavorite 함수가 성공되지 않은 시점이므로 이전 상태를 가지게 된다. 왜 이런 현상이 일어날까.

mutateFn 콜백을 다시 살펴보자

위의 코드에서 버그가 일어났던 이유는 mutateFn 콜백에서 비동기함수를 호출하고 아무것도 반환하지 않았기 때문이다.  비동기 함수의 반환값(프로미스)을 반환하거나 비동기 함수를 await 했다면 정상 작동할 것이다.

일관된 코드 스타일의 중요성

일정이 빠듯하면 이런 잔실수들이 꼭 생긴다. 이번 실수는 화살표 함수에서 return 키워드 유무를 혼용하면서 나온 실수인 것 같다. 화살표 함수에서 스코프(중괄호)가 없으면 return 키워드를 쓰지 않고 반환할 수 있지만 위의 코드처럼 스코프가 있음에도 return 키워드를 깜빡해서 잘못된 동작을 야기했다. 고차함수 사용시에도 종종 이런 실수가 있는데, 특별한 경우가 아니라면 return을 명시적으로 사용하는 습관을 들여서 실수를 줄여야겠다.

Comments