TIL archiving ···.ᐟ/React

TanStack Query(React Query)

dayoung-archive 2024. 10. 21. 10:09

📌 탄스택 쿼리

서버로부터 데이터 가져오기, 데이터 캐싱, 캐시 제어 등 데이터를 쉽고 효율적으로 관리할 수 있는 라이브러리.

React Query라는 이름으로 시작했지만, v4부터 Vue나 Svelte 등의 다른 프레임워크에서도 활용할 수 있도록 기능이 확장되며 TanStack Query라는 이름으로 변경되었다.

 

1. 탄스택 쿼리의 특징

  • 데이터 가져오기 및 캐싱
  • 동일 요청의 중복 제거
  • 신선한 데이터 유지
  • 무한 스크롤, 페이지네이션 등의 성능 최적화
  • 네트워크 재연결, 요청 실패 등의 자동 갱신
  • 옵션에 쿼리 키(queryKey) 필수!!

 

 

2. useQuery

가장 기본적인 쿼리 훅, 컴포넌트에서 데이터를 가져올 때 사용

const 반환 = useQuery<데이터타입>(옵션)

 

 

 

useQuery 를 사용해 JSONPlaceholder라는 무료 REST API에서 사용자 목록 가져오기

// UsersList.js

import React from "react";
import { useQuery } from "@tanstack/react-query";

function UsersList() {
  const { data, isLoading, error } = useQuery({
    queryKey: ["users"],   // 쿼리key
    queryFn: async () => {  // queryFn: query Function으로 데이터를 가져오는 쿼리 함수
      const response = await fetch(
        "https://jsonplaceholder.typicode.com/users"
      );
      if (!response.ok) {
        throw new Error("네트워크 응답이 좋지 않습니다");
      }
      return response.json();
    },
  });

  if (isLoading) {  // isLoading: 쿼리 함수의 첫 번째 가져오기가 진행 중
    return <div>Loading...</div>;
  }

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

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

export default UsersList;
// App.js

import React from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import UsersList from "./components/UsersList"; // UsersList 컴포넌트 가져오기

const queryClient = new QueryClient();   // QueryClient : 커스텀 쿼리 클라인트 연결.

export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <h1>사용자 리스트</h1>
      <UsersList />
    </QueryClientProvider>
  );
}

// QueryClient를 생성 후, QueryClientProvider로 App을 감싸주면 앱 내의 모든 컴포넌트에서 
// React Query의 기능을 사용할 수 있다.

사용자 리스트가 잘 불러와지는 걸 확인할 수 있다.

 

 

useQuery의 여러가지 옵션 참고

https://tanstack.com/query/v5/docs/framework/react/reference/useQuery

 

 

3.useInfiniteQuery

더 보기 버튼으로 추가 데이터를 더 가져오거나, 무한 스크롤 기능, 페이지네이션을 구현하기 쉽게 해주는 훅

const 반환 = useInfiniteQuery<페이지타입>(옵션)

 

useInfiniteQuery 를 사용해 더 보기 버튼을 클릭하여 다음 페이지의 사용자 목록을 가져오기

// UsersList.js

import React from "react";
import { useInfiniteQuery } from "@tanstack/react-query";

const fetchUsers = async ({ pageParam = 1 }) => {
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/users?_page=${pageParam}&_limit=5`
    // 페이지가 업데이트 되야되니까 동적변수로 받고, 개수는 5개로 설정
  );
    if (!response.ok) {
        throw new Error("네트워크 응답이 좋지 않습니다");
      }

  return response.json();
};

export default function UsersList() {
  const {
    data,
    isLoading,
    isFetching,
    hasNextPage,
    fetchNextPage,
    isError,
    error,
  } = useInfiniteQuery({
    queryKey: ["users"],
    queryFn: fetchUsers,
    getNextPageParam: (lastPage, pages) => {
      // 다음 페이지가 있으면 페이지 번호 반환, 없으면 undefined 반환
      return pages.length < 10 ? pages.length + 1 : undefined; //  최대 10페이지
    },
  });

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

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

  return (
    <div>
      <ul>
        {data.pages.map((page, index) => (
          <React.Fragment key={index}>
            {page.map((user) => (
              <li key={user.id}>{user.name}</li>
            ))}
          </React.Fragment>
        ))}
      </ul>
      {hasNextPage && (
        <button onClick={fetchNextPage} disabled={isFetching}>
          {isFetching ? "Loading..." : "더 보기"}
        </button>
      )}
    </div>
  );
}

더 보기를 누르면 5개씩 사용자 이름을 더 불러오는 것을 확인 할 수 있다.

 

 

useInfiniteQuery의 여러가지 옵션 참고

https://tanstack.com/query/v5/docs/framework/react/reference/useInfiniteQuery

 

 

 

4. useMutation

데이터 변경 작업(생성, 수정, 삭제 등)을 위한 훅, 데이터 변경 작업을 처리하고 다양한 성공, 실패, 로딩 등의 상태를 얻을 수 있다.

💡 쿼리(useQuery)는 '가져오기'에 집중하는 반면, 변이(useMutation)는 '보내기'에 집중하는 훅으로 이해하면 쉽다!

 

const 반환 = useMutation(옵션)
// UsersList.js

import React from "react";
import { useQuery } from "@tanstack/react-query";

const fetchUsers = async () => {
  const response = await fetch("https://jsonplaceholder.typicode.com/users");
  if (!response.ok) {
        throw new Error("네트워크 응답이 좋지 않습니다");
      }
  return response.json();
};

export default function UsersList() {
  const {
    data = [],
    isLoading,
    isError,
    error,
  } = useQuery({
    queryKey: ["users"],
    queryFn: fetchUsers,
  });

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

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

  return (
    <ul>
      {data.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}
// AddUser.js

import React, { useState } from "react";
import { useMutation, useQueryClient } from "@tanstack/react-query";

const addUser = async (newUser) => {
  // 실제 API가 아니므로, 반환하는 데이터를 수정
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ id: Date.now(), ...newUser });
    }, 500);
  });
};

export default function AddUser() {
  const [name, setName] = useState("");
  const queryClient = useQueryClient();

  const mutation = useMutation({
    mutationFn: addUser,
    onSuccess: (newUser) => {
      // 사용자 목록 무효화 및 새로운 사용자 추가
      queryClient.setQueryData(["users"], (old) => [...old, newUser]);
    },
    onError: (error) => {
      console.error("사용자 추가에 실패했습니다:", error);
    },
  });

  const handleSubmit = (e) => {
    e.preventDefault();
    if (name) {
      mutation.mutate({ name });
      setName("");
    }
  };

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <input
          type="text"
          value={name}
          onChange={(e) => setName(e.target.value)}
          placeholder="사용자 이름을 입력하세요."
        />
        <button type="submit" disabled={mutation.isLoading}>
          {mutation.isLoading ? "추가 중..." : "추가하기"}
        </button>
      </form>
      {mutation.isError && <div>오류: {mutation.error.message}</div>}
      {mutation.isSuccess && <div>사용자가 추가되었습니다!</div>}
    </div>
  );
}

 

 

useMutation의 여러가지 옵션 참고

https://tanstack.com/query/v5/docs/framework/react/reference/useMutation

'TIL archiving ···.ᐟ > React' 카테고리의 다른 글

Storybook  (0) 2024.10.22
[React 연습] 간단한 카운터 & Todo 만들기  (1) 2024.10.16
상태 관리 라이브러리 Recoil  (2) 2024.10.15
Context API  (0) 2024.10.14
React Router - useNavigate()  (0) 2024.10.10