리액트 쿼리를 쓰면 좋다고 해서 공부하려고 하는데 아직 개념이 덜 잡힌거 같다 🥲
React Query는 데이터 Fetching, 캐싱, 동기화, 서버 쪽 데이터 업데이트 등을 쉽게 만들어 주는 React 라이브러리이다.
=>기존에 Redux, Mobx, Recoil과 같은 다양하고 훌륭한 상태 관리 라이브러리들이 있긴 하지만, 클라이언트 쪽의 데이터들을 관리하기에 적합할 순 있어도 서버 쪽의 데이터들을 관리하기에는 적합하지 않은 점들이 있어 해결하기 위해 React-Query가 만들어졌다.
React-Query 장점
- 서버사이드 상태 관리와 관련된 여러 가지 반복적인 작업들을 손쉽게 처리할 수 있다
- 데이터뿐만 아니라 isIdle, isLoading, isFetching, isSuccess, isError등과 같은 여러 가지 부가적인 상태 값들도 제공한다
- query key를 통하여 데이터 캐싱과 각 쿼리 간에 디펜던시 조작도 손쉽게 할 수 있다.
- useEffect로 처리했던 여러 가지 상황들을 refetchOnMount, refetchOnReconnect, refetchOnWindowFocus와 같은 옵션으로 쉽게 처리할 수 있다.
- keepPreviousData 옵션이나 useInfiniteQuery를 사용하여 페이징 기능도 쉽게 구현할 수 있다.
- useQueries를 사용하여 병렬처리가 가능하다.
- 서버 쪽 데이터를 가비지 컬렉션을 이용하여 자동으로 메모리를 관리해준다
- 구조적 공유를 통해 쿼리의 결과를 기억해준다.
💡클라이언트 상태(Client State) 와 서버 상태(Server State)는 완전히 다르며클라이언트 상태는 컴포넌트에서 관리하는 각각의 input 값으로 예를 들 수 있고서버 상태는 database에 저장되어있는 데이터로 예를 들 수 있다.
1. Client State
Ownership이 Client에 있다.
- Client에서 소유하며 온전히 제어가능함
- 초기값 설정이나 조작에 제약사항 없음
- 다른 사람들과 공유되지 않으며 Client 내에서 UI/UX 흐름이나 사용자 인터랙션에 따라 변할 수 있음
- 항상 Client 내에서 최신 상태로 관리됨
2. Server State
Ownership이 Server에 있다.
- Client에서 제어하거나 소유되지 않는 원격의 공간에서 관리되고 유지됨
- Fetching이나 Updating에 비동기 API가 필요함
- 다른 사람들과 공유되는 것으로 사용자가 모르는 사이에 변경될 수 있음
- 신경 쓰지 않는다면 잠재적으로 "out of date"가 될 가능성을 지님
React-Query 상태
fesh : 새롭게 추가된 쿼리 & 만료되지 않은 쿼리 => 컴포넌트가 마운트, 업데이트되어도 데이터 재요청 ❌
fetching : 요청 중인 쿼리
stale : 만료된 쿼리 => 컴포넌트가 마운트, 업데이트되면 데이터 재요청 ⭕️
inactive : 비활성화된 쿼리 => 특정 시간이 지나면 가비지 컬렉터에 의해 제거
delete - 가비지 컬렉터에 의해 캐시에서 제거된 쿼리
QueryClientProvider
- 리액트 쿼리 사용을 위해 QueryClientProvider 를 최상단에서 감싸야한다
- 쿼리 인스턴스를 생성 후 client={queryClient} 작성한다
<code />
import { QueryClient, QueryClientProvider } from "react-query";
const queryClient = new QueryClient();
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Home />
</QueryClientProvider>
);
}
useQuery
<javascript />
import { useQuery } from "react-query";
// 주로 사용되는 3가지 return 값 외에도 더 많은 return 값들이 있다.
const { data, isLoading, error } = useQuery(queryKey, queryFn, options)
QueryKey
- QueryKey 를 기반으로 데이터 캐싱을 관리한다.
- 문자열 또는 배열로 지정할 수 있다.
<code />
// 문자열
useQuery('todos', ...)
// 배열
useQuery(['todos'], ...)
- 쿼리가 변수에 의존하는 경우에는 QueryKey 에도 해당 변수를 추가해주어야한다.
<code />
const { data, isLoading, error } = useQuery(['todos', id], () => axios.get(`http://.../${id}`));
Query Functions
- useQuery 의 두번째 인자에는 promise 를 반환하는 함수를 넣어주어야한다.
- 거의 첫번째나 두번째 방식으로 코드를 작성하고 있다.
<code />
useQuery('todos', fetchTodos);
useQuery(['todos', todoId], () => fetchTodoById(todoId));
useQuery(['todos', todoId], async () => {
const data = await fetchTodoById(todoId);
return data
});
useQuery(['todos', todoId], ({ queryKey }) => fetchTodoById(queryKey[1]));
Query Options
enabled (boolean)
- enabled 는 쿼리가 자동으로 실행되지 않게 설정하는 옵션이다.
- 아래의 코드는 id가 존재할 때만 쿼리 요청을 한다는 의미의 코드이다.
<code />
const { data } = useQuery(
['todos', id],
() => fetchTodoById(id),
{
enabled: !!id,
}
);
retry (boolean | number | (failureCount: number, error: TError) => boolean)
- retry 는 실패한 쿼리를 재시도하는 옵션이다.
- 기본적으로 쿼리 실패시 3번 재시도 한다.
- true 로 설정하면 쿼리 실패시 무한 재시도하고 false로 설정하면 재시도를 하지 않는다.
staleTime (number | Infinity)
- staleTime 은 데이터가 fresh 상태로 유지되는 시간이다. 해당 시간이 지나면 stale 상태가 된다.
- default staleTime은 0 이다.
- fresh 상태에서는 쿼리가 다시 mount 되어도 fetch가 실행되지 않는다.
cacheTime (number | Infinity)
- cacheTime 은 inactive 상태인 캐시 데이터가 메모리에 남아있는 시간이다. 이 시간이 지나면 캐시 데이터는 가비지 컬렉터에 의해 메모리에서 제거된다.
- default cacheTime 은 5분이다.
refetchOnMount (boolean | "always")
- refetchOnMount 는 데이터가 stale 상태일 경우 마운트 시 마다 refetch를 실행하는 옵션이다.
- default true
- always 로 설정하면 마운트 시 마다 매번 refetch 를 실행한다.
refetchOnWindowFocus (boolean | "always")
- refetchOnWindowFocus 는 데이터가 stale 상태일 경우 윈도우 포커싱 될 때 마다 refetch를 실행하는 옵션이다.
- 예를 들어, 크롬에서 다른 탭을 눌렀다가 다시 원래 보던 중인 탭을 눌렀을 때도 이 경우에 해당한다. 심지어 F12로 개발자 도구 창을 켜서 네트워크 탭이든, 콘솔 탭이든 개발자 도구 창에서 놀다가 페이지 내부를 다시 클릭했을 때도 이 경우에 해당한다.
- default true
- always 로 설정하면 항상 윈도우 포커싱 될 때 마다 refetch를 실행한다는 의미이다.
1.
2. 가장기본적이 리액트 쿼리 예제
<javascript />
import {
QueryClient,
QueryClientProvider,
useQuery,
} from '@tanstack/react-query'
const queryClient = new QueryClient()
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Example />
</QueryClientProvider>
)
}
function Example() {
const { isLoading, error, data } = useQuery({
queryKey: ['repoData'],
queryFn: () =>
fetch('https://api.github.com/repos/tannerlinsley/react-query').then(
(res) => res.json(),
),
})
if (isLoading) return 'Loading...'
if (error) return 'An error has occurred: ' + error.message
return (
<div>
<h1>{data.name}</h1>
<p>{data.description}</p>
<strong>👀 {data.subscribers_count}</strong>{' '}
<strong>✨ {data.stargazers_count}</strong>{' '}
<strong>🍴 {data.forks_count}</strong>
</div>
)
}
useState와 useEffect 훅을 사용하지 않는다는 점=> useQuery에는 어플리케이션에서 활용할 수 있는 다양한 값 (로딩 상태, 에러 응답, 반환된 데이터 등)들이 이미 포함되어 있기 때문이다
'React' 카테고리의 다른 글
느린컴포넌트 성능 향상가능한 useDeferredValue& useTransition(REACT 18) (6) | 2023.03.13 |
---|---|
ref를 prop으로 넘기기( forWardRef) (6) | 2023.03.10 |
라이프사이클과 useEffect (6) | 2023.02.16 |
React Hooks:useContext(전역 상태관리) (6) | 2023.02.14 |
Axios 인터셉터 사용법 (6) | 2023.02.13 |