React Query

·2024년 1월 6일

🥰 React Query란?

React에서 데이터의 fetching, caching, synchronizing, updating server state를 위한 라이브러리이다. 즉, axios와 같이 단순한 API fetching 보다는, 서버 상태 자체를 관리하는 라이브러리라고 할 수 있다.

🧐 React Query는 왜 사용할까?

Recoil, Redux에서 관리하는 Client Server와는 달리, Server State에서는

  • Client에서 제어할 수 없는 원격의 공간에서 관리
  • 비동기 API 필요
  • 사용자가 모르는 사이에 업데이트 될 수 있음
  • 데이터의 유효기간이 지난 상태가 될 수 있음
    와 같은 특징을 지닌다.
    그렇기 때문에,
    캐싱, 중복 호출 제거, 만료된 데이터 제거, 만료 시점 인지, 데이터의 업데이트, 성능 최적화와 같은 내용들이 화두에 떠오르게 되며, 이러한 Server State를 Client에서 관리하는 것이 옳지 않다는 주장하에 React Query가 등장하게 되었다.


    즉 ReactQuery는
    Server State를 관리하기 위해 등장한 라이브러리이다.

😍 React Query 장점

프론트엔드 개발자가 구현하기 어려운 일들을 수행

  • 캐싱
  • 페이지네이션, 무한 스크롤
  • 비동기 과정의 선언적 관리
  • 업데이트를 하면 자동적으로 데이터를 다시 get하는 로직
  • 데이터의 stale Time 을 사용하여 다시 get (invalidateQueries)
  • 동일 데이터의 중복 호출 허용 시간 조절

😵 React Query 시작하기

yarn add react-query

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { QueryClient, QueryClientProvider } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";

const queryClient = new QueryClient();		// 인스턴스 생성

ReactDOM.render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      {/* devtools */}
      <ReactQueryDevtools initialIsOpen={false} position="bottom-right" />
      <App />
    </QueryClientProvider>
  </React.StrictMode>,
  document.getElementById("root")
);

yarn을 이용해 라이브러리를 설치한 후,
캐시를 위한 QueryClient 인스턴스와 접근이 가능하도록 해주는
QueryClientProvider를 설정한다.

😦 React Query 기본 개념

❤️ 쿼리 데이터의 라이프 사이클

  • fetching: 요청중인 쿼리
  • fresh: 만료되지 않은 쿼리
  • stale: 만료된 쿼리
  • inactive: 사용하지 않는 쿼리, 일정 시간이 지나면 가비지 콜렉터가 제거하게 된다.
  • delete: 제거된 쿼리

🧡 주요 개념

  • Queries: 비동기 데이터에 대한 선언적 종속석을 가지고, GET, POST 메서드와 연관됨
  • Mutations: 서버 데이터 수정과 관련된 기능
  • Query Invalidation: 캐싱된 쿼리 데이터가 유효한지 여부를 판단

💛 stale 쿼리

useQuery에서 가져온 데이터의 기본적인 상태이다.
이때,

  • 새로운 쿼리 인스턴스 마운트
  • 브라운저 윈도우 refocus
  • 네트워크 연결
  • retecthInteral 옵션
    이 있을 때, 다시 백그라운드에서 가져온다.
  • useQuery는 백그라운드에서 3회 시도를 하고, 그 이상 실패하면 에러처리를 한다.
  • inactive 쿼리의 기본 stale Time은 5분이다.

😁 useQuery

데이터를 get하기 위한 api(데이터 패치 hooks)이며 비동기로 작동한다. ( 동기적 실행: enabled)
따라서, 여러 개의 비동기 query가 있다면 useQueries를 선택하는 옵션도 존재함

❤️ useQuery 인자

useQuery(['todos'], fetchAllTodos)
useQuery(['todos', todoId], () => fetchTodoById(todoId))
useQuery(['todos', todoId], async () => {
  const data = await fetchTodoById(todoId)
  return data
})
useQuery(['todos', todoId], ({ queryKey }) => fetchTodoById(queryKey[1]))
  1. 첫번째 파라미터(queryKey): unique key 문자열 혹은 문자열의 배열로 할당한다. key가 같은 쿼리들은 하나의 요청으로 값을 공유하고, 다른 컴포넌트에서 해당 키를 사용하면 호출 가능
    문자열 배열인 경우 배열의 두번째 값은 query 함수 내부의 파라미터로 전달된다.

  2. 두번째 파라미터(queryFn): 비동기 함수, Promise를 처리 (api호출 함수로 axios, fetch함수)

  3. 세번째 파라미터 (options) : 필수는 아님
  • enabled: 데이터 자동 패치 여부, 기본값: true
  • retry: 재요청 여부 횟수, 기본적으로는 3번
  • retryNumber : 재요청까지의 대기 시간
  • staleTime: 데이터가 fresh한 상태로 유지되는 기간
  • cacheTime: inactive 상태인 캐시 데이터가 메모리에 남아있는 시간, 기본값은 5분
  • refetchOnMount, refetchOnWindowFocus, refetchOnReconnect: refetch 실행 여부 기본값은 true
  • retrechInterval: refetch가 발생하는 간격
  • onSuccess: 데이터 fetch 성공 시 실행되는 콜백, response 데이터가 들어있다
  • onError: 데이터 fetch 실패 시 실행되는 콜백으로 error 정보가 포함
  • onSettled: 데이터 fetch 완료 시 실행되는 콜백, 성공 실패 여부와는 상관없다.
  • select: 데이터를 변형시키는 옵션 메서드

  1. return 값: api 성공, 실패 여부, api return 값을 포함한 객체
  • data: 응답받은 데이터
  • error: 실패 에러 정보
  • refetch: stale이나 cache 설정을 무시하고 데이터 refetch
  • status: idle, loading, error, success 등 fetch 상태
  • isLoading, isFetching, isIdle, isSuccess, isError

🧡 useQuery 예시

function Todos() {
const {  data, isError, error, isLoading } = useQuery("todos", fetchTodoList);

  if (isLoading) {
   return <span>Loading...</span>;
 }

  if (isError) {
    return <span>Error: {error.message}</span>;
  }

  return (
    <ul>
      {data.map(todo => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  );
}

😶 useMutation

데이터를 POST, PUT, DELETE 하여 수정하기 위한 api,
즉, 서버의 데이터 변경 작업을 요청할 때 사용하는 hook이다.
mutate: 데이터의 변화

❤️ useMutation 인자

import { useMutation } from "react-query";

const { data, isLoading, mutate } = useMutation(mutationFn, options);
  1. 첫번째 파라미터(mutationFn): mutate를 위한 패치 함수, POST, PUT, DELETE 및 각 요청에 필요한 인자들이 포함된다.

  2. 두번째 파라미터(options)
  • onMutate: variables => Promise<Context> | void mutation 전에 실행되는 함수로 미리 렌더링하고자 할 때 유용, 반환하는 값을 아래 함수들의 context로 활용 가능
  • onSuccess: (data, variables, context?) => void.mutation이 성공하고 결과를 전달할 때 실행
  • onError: (error, variables, context?) => void mutation이 실패했을 때 에러를 전달
  • onSettled:data, error, variables, context?) => void mutation의 성공 실패 여부와 상관없이 완료되었을 때 실행

  1. 반환값
  • mutate: (variables, { onSuccess, onError, onSettled }) => void mutation을 실행시키는 메서드로 가장 많이 쓰임
    variables 매개변수가 mutationFn으로 전달되며, 옵션의 메서드들은 Options 함수들과 동일하다.
  • mutateAsync : mutate 결과를 Promise로 반환, catch()로 에러 핸들링을 직접 해야한다.

🧡 useMutation 예시

import { useState, useContext, useEffect } from "react";
import loginApi from "api";
import { useMutation } from "react-query";

const Index = () => {
  const [id, setId] = useState("");
  const [password, setPassword] = useState("");

  const loginMutation = useMutation(loginApi, {
    onMutate: variable => {
      console.log("onMutate", variable);
      // variable : {loginId: 'xxx', password; 'xxx'}
    },
    onError: (error, variable, context) => {
      // error
    },
    onSuccess: (data, variables, context) => {
      console.log("success", data, variables, context);
    },
    onSettled: () => {
      console.log("end");
    }
  });

  const handleSubmit = () => {
    loginMutation.mutate({ loginId: id, password });
  };

  return (
    <div>
      {loginMutation.isSuccess ? "success" : "pending"}
      {loginMutation.isError ? "error" : "pending"}
      <input type="text" value={id} onChange={e => setId(e.target.value)} />
      <input
        type="password"
        value={password}
        onChange={e => setPassword(e.target.value)}
      />
      <button onClick={handleSubmit}>로그인</button>
    </div>
  );
};

export default Index;

😖 출처 😖
출처1
출처2

profile
new blog: https://hae0-02ni.tistory.com/

0개의 댓글