본문 바로가기
  • 살짝 구운 김 유나
Web/React

[React] React Query

by yunae 2023. 1. 18.

[공식문서]

https://tanstack.com/query/latest

 

TanStack Query | React Query, Solid Query, Svelte Query, Vue Query

Powerful asynchronous state management, server-state utilities and data fetching for TS/JS, React, Solid, Svelte and Vue

tanstack.com

 

Tanstack Query

강력한 비동기 상태 라이브러리

서버에 있는 데이터를 가져와서 보여주는 경우, 네트워크의 데이터를 가져와서 상태관리를 할 수 있게 해주는 라이브러리

 

 

 

커스텀 훅의 문제점 2가지

1. cache가 되지 않는 문제

- 훅을 호출할 때마다 새롭게 데이터를 받아옴

- 아래의 경우 두 개의 컴포넌트가 각각 다른 상태 정보를 가져오기 때문에 2번의 fetching이 발생

 

 

2. 통신에 실패했을 때 재시동할 수 없는 문제

=> 이러한 문제를 리액트 쿼리가 해결해 준다!!!

 

설치

$ npi @tanstack/react-query
# or
$ yarn add @tanstack/react-query

 

사용방법

// 쿼리를 가지고 와서
import {
  QueryClient,
  QueryClientProvider,
  useQuery,
} from '@tanstack/react-query'

// 클라이언트 초기화
const queryClient = new QueryClient()

export default function App() {
  return (
  	// 어플리케이션 감싸주기
    <QueryClientProvider client={queryClient}>
      <Example />
    </QueryClientProvider>
  )
}

function Example() {
  // useQuery 사용하기
  const { isLoading, error, data } = useQuery({
    queryKey: ['repoData'],
    queryFn: () =>
      // 요청할 주소만 알려주면 로딩중, 에러, 데이터가 있는지 알려줌 + 캐싱
      fetch('https://api.github.com/repos/tannerlinsley/react-query').then(
        (res) => res.json(),
      ),
  })

 

 

 

적용하기

import React from 'react';
import './App.css';
import MainProducts from './components/MainProducts';
// App.js

import {
  QueryClient,
  QueryClientProvider,
} from '@tanstack/react-query'

const queryClient = new QueryClient()

export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <MainProducts />
    </QueryClientProvider>
  )
}
// Products.jsx

import { useQuery } from '@tanstack/react-query';
import React, { useState } from 'react';

export default function Products() {
  const [checked, setChecked] = useState(false);
  // useQuery를 사용해서 로딩중, 에러, 데이터에 대한 정보 가져오기
  // 'product'라는 문자열로 키 지정
  const {isLoading, error, data: products} = useQuery(['products'], async ()=>{
    console.log('...fetching')
    return fetch(`data/products.json`)
      .then((res) => res.json())
  })
  // const [loading, error, products] = useProducts({ salesOnly: checked });
  const handleChange = () => setChecked((prev) => !prev);

  if (isLoading) return <p>Loading...</p>;

  if (error) return <p>{error}</p>;

  return (
    <>
      <label>
        <input type='checkbox' checked={checked} onChange={handleChange} />
        Show Only 🔥 Sale
      </label>
      <ul>
        {products.map((product) => (
          <li key={product.id}>
            <article>
              <h3>{product.name}</h3>
              <p>{product.price}</p>
            </article>
          </li>
        ))}
      </ul>
    </>
  );
}

커스텀 훅을 사용했을 때는 각각의 컴포넌트에서 useEffect가 실행 되었기 때문에 서로 다른 상태 정보를 가지고 있었지만,

리액트 쿼리를 사용하면 네트워크 통신 별로 고유한 키를 제공하기 때문에 키 이름 아래에 상태 정보를 저장!

 

두 개의 컴포넌트가 동일한 쿼리 키를 사용하므로 두번째 컴포넌트는 캐싱된 상태 정보를 가져올 수 있는 것!

=> 딱 한번만 네트워크 통신이 발생!!

 

 

 

 

useQuery

1. 쿼리 키

2.  네트워크에서 데이터를 반환하는 함수

3. 옵션 (Devtools에서 다룸)

const {
	isLoading,
    error,
    data: products
} = useQuery(['products', checked], async ()=>{
    console.log('...fetching')
    return fetch(`data/${checked? 'sale_' : ''}products.json`)
      // 
      .then((res) => res.json())
  })

 

 

 

 

 Query Keys

리액트 쿼리는 키에 의존 => key들을 잘 명시하고 분리하는 것이 중요!!

 

// A list of todos
useQuery({ queryKey: ['todos'], ... })

// Something else, whatever!
useQuery({ queryKey: ['something', 'special'], ... })
// An individual todo
useQuery({ queryKey: ['todo', 5], ... })

// An individual todo in a "preview" format
useQuery({ queryKey: ['todo', 5, { preview: true }], ...})

// A list of todos that are "done"
useQuery({ queryKey: ['todos', { type: 'done' }], ... })

=> 원하는 조건이나 세부 상태 별로 키를 조합해서 사용하는 것도 가능

 

예시

export default function Products() {
  const [checked, setChecked] = useState(false);
  // 데이터 직접 사용해서 특정 조건일 때 다른 요청을 보낼 수도 있다...
  const {isLoading, error, data: products} = useQuery(['products', checked], async ()=>{
    console.log('...fetching')
    return fetch(`data/${checked? 'sale_' : ''}products.json`)
      .then((res) => res.json())
  })
  
  ...생략

 

 

=> windowFocus가 될 때마다 fetching이 계속 되는 문제,,,가 생거버림,,,ㅎ

 

 

 

 

Devtools

설치

$ npm i @tanstack/react-query-devtools
# or
$ yarn add @tanstack/react-query-devtools

 

사용방법

import { ReactQueryDevtools } from '@tanstack/react-query-devtools'

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      {/* The rest of your application */}
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  )
}

 

<ReactQueryDevtools initialIsOpen={true} />

초기값을 true로 바꿔주면 다음과 같이 개발 툴에서 여러 정보를 확인할 수 있음

react query devtools

 

 

 

 

 

Important Defaults

 

1. useQuery를 사용하면 캐시된 데이터를 'stale'로 간주

=> 한 번 받아온 데이터를 얼마동안 캐싱할 것인지 'staleTime' 옵션 사용

2. 원하지 않았지만 refech가 발생하는 경우에는,

=> refetchOnMount, refetchOnWindowFocus, refetchOnReconnect, refetchInterval과 같은 옵션 사용

3. useQuery를 5분간 사용하지 않으면 캐싱된 데이터를 지움

=> cacheTime 옵션 사용

4. 쿼리는 네트워크 통신에 실패했을 때 시간 설정

=> retry, retryDelay 옵션 사용

 

 

staleTime 예시

- useQuery의 세번째 인자로 staleTime 지정

export default function Products() {
  const [checked, setChecked] = useState(false);
  const {isLoading, error, data: products} = useQuery(['products', checked], async ()=>{
    console.log('...fetching')
    return fetch(`data/${checked? 'sale_' : ''}products.json`)
      .then((res) => res.json())
  }, {
    // 5분 유지
    staleTime: 1000 * 60 * 5,
  });

=> 데이터가 fresh로 5분간 유지됨

 

+ 클라이언트에서 api post 요청으로 데이터를 업데이트 한 경우 캐시를 Invaild로 변경할 수 O

버튼을 클릭하면 'product'를 키로 가지고 있는 모든 컴포넌트는 다시 fetch!

import { useQueryClient } from '@tanstack/react-query';
import React, { useState } from 'react';
import Products from './Products';

export default function MainProducts() {
  const [showLeftProducts, setShowLeftProducts] = useState(true);
  const [showRightProducts, setShowRightProducts] = useState(true);
  const client = useQueryClient();
  return (
    <main className='container'>
      ...생략
      </div>
      <button onClick={()=>{client.invalidateQueries(['products'])}}>정보가 업데이트 되었음!</button>
    </main>
  );
}

 

 

 

 

 

 

겨울 가지마러라 난 눈이 좋단말야

혼자 음악에 맥주 한 캔 하면서 일하기,, 얼마만이야,, 좀 행복하다,,

앞으로 강제 갓생 한 달 파이팅,, 잠은 죽어서 자자 ㅎ

 

'Web > React' 카테고리의 다른 글

[React] i18n으로 다국어 지원하기 (i18next, react-i18next)  (0) 2023.10.22
React를 위한 상태관리 라이브러리 - Recoil  (0) 2023.09.12
[React] Redux  (0) 2022.12.19
[React] ajax  (0) 2022.12.15
[React] Lifecycle  (0) 2022.12.15

댓글