React Query - 사용자 경험 개선 UI

박정호·2023년 1월 28일
2

React Query

목록 보기
14/14
post-thumbnail

🚀 Start

useQuery의 속성인 isLoading, isError를 통해 데이터 상태를 UI로 구현하여 사용자 경험을 개선시킬 수 있었다. 하지만, query 요청이 필요한 컴포넌트마다 useQuery의 속성을 사용해야하므로 이는 비효율적이라는 생각이 들었다.

다행히 React Query에서는 useIsFetchinguseIsMutating이라는 기능을 통해 전역적으로 데이터 사태에 대해 관찰할 수 있다.

  • useIsFetching은 애플리케이션이 백그라운드에서 로드하거나 가져오는 query 수를 반환

  • useIsMutating은 애플리케이션이 가져오는 mutate 수를 반환

따라서, 데이터를 요청하거나 업데이트할 경우에 데이터가 로드되는 동안 사용자에게 보여줄 로딩 UI를 출력할 수 있다.


또한 데이터 요청에 대한 응답 결과를 사용자에게 보여주면 더욱 사용자 경험이 개선될 것이다. 예를 들어, 데이터를 수정,등록,삭제 등의 사용자 요청에 대한 응답을 보여주는 Alert UI를 사용하여 데이터가 성공적으로 응답했는지, 실패했는지를 구분할 수 있을 것이다.

해당 기능은 onSuccess, onError 속성을 통해 쉽게 구현 가능하다.
(물론, suspense를 이용할 수도 있고, 다른 방법도 존재하겠쥬.)



Spinner UI

spinner 컴포넌트

export function Loading(): ReactElement {
  const isFetching = useIsFetching();
  const isMutating = useIsMutating();

  const display = isFetching || isMutating ? "inherit" : "none";

  return (
    <Spinner
      thickness="4px"
      speed="0.65s"
      emptyColor="gray.200"
      color="blue.500"
      size="xl"
      role="status"
      position="fixed"
      zIndex="9999"
      top="50%"
      left="50%"
      transform="translate(-50%, -50%)"
      display={display}
    >
      <Text>Loading...</Text>
    </Spinner>
  );
}

_app.js

const App = ({ Component, pageProps }) => {
  ..

  return (
      <QueryClientProvider client={getClient()}>
        <Hydrate state={pageProps.dehydratedState}>
          <Loading />
          <Component {...pageProps} />
        </Hydrate>
      </QueryClientProvider>
  );
};

Toast UI

Custom Toast

// hooks/ useCustomToast.ts
import { ToastId, useToast, UseToastOptions } from "@chakra-ui/react";

interface UseCustomToast {
  (options?: UseToastOptions | undefined): string | number | undefined;
  close: (id: ToastId) => void;
  isActive: (id: ToastId) => boolean | undefined;
}

export function useCustomToast(): UseCustomToast {
  return useToast({
    isClosable: true,
    variant: "subtle",
    position: "bottom",
  });
}

onSucess, onError

 const toast = useToast();
 
 // hooks/useDeleteMessage.ts
 const { mutate: onDelete } = useMutation(
    (id: string) => fetcher(DELETE_MESSAGE, { id, userId }),
    {
      onSuccess: () => {
       	...
        toast({
          title: "Content has been deleted.",
          status: "success",
          isClosable: true,
        });
      },
    }
  );

 // hooks/useCreateMessage.ts
 const { mutate: onCreate } = useMutation(
    ({ text }: { text: string }) => fetcher(CREATE_MESSAGE, { text, userId }),
    {
      onSuccess: () => {
      	...
        toast({
          title: "Content has been posted.",
          status: "success",
          isClosable: true,
        });
      },
      onError: () => {
        toast({
          title: "You must enter the content.",
          status: "warning",
          isClosable: true,
        });
      },
    }
  );

 // hooks/useUpdateMessage.ts
 const { mutate: onUpdate } = useMutation(
    ({ text, id }: { text: string; id?: string }) => fetcher(UPDATE_MESSAGE, { text, id, userId }),
    {
      onSuccess: () => {
    	...
        toast({
          title: "Content has been updated.",
          status: "success",
          isClosable: true,
        });
      },
    }
  );


실제 구현 화면

profile
기록하여 기억하고, 계획하여 실천하자. will be a FE developer (HOME버튼을 클릭하여 Notion으로 놀러오세요!)

0개의 댓글