[공식문서]
https://tanstack.com/query/latest
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로 바꿔주면 다음과 같이 개발 툴에서 여러 정보를 확인할 수 있음
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 |
댓글