반응형
React Query
server state를 관리하는 라이브러리로, 리액트 프로젝트에서 서버와 클라이언트 사이 비동기 로직들을 손쉽게 다루게 해주는 도구이다.
데이터 fetching, 백그라운드에서 데이터 업데이트, Optimistic Updates, 캐싱과 key를 통한 전역 상태와 같은 사용 등 많은 유용한 기능을 제공한다.
리액트 쿼리에서는 API 통신을 위한 여러 함수를 useQuery와 같은 Hook 인터페이스로 제공한다.
import React from "react";
import ReactDOM from "react-dom";
import { QueryClient, QueryClientProvider, useQuery } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
const queryClient = new QueryClient();
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Example />
</QueryClientProvider>
);
}
function Example() {
const { isLoading, error, data, isFetching } = useQuery("repoData", () =>
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>{isFetching ? "Updating..." : ""}</div>
<ReactQueryDevtools initialIsOpen />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
useQuery
- 쿼리 키: 문자열이나 배열을 이용한다. 캐싱 처리에 있어서 중요한 개념이다.
- 쿼리 함수: Promise를 리턴하는 함수이다. ex) axios, fetch
- 옵션: useQuery 기능을 제어한다.
이 때 쿼리 키가 다르면 호출하는 API가 같더라도 캐싱을 별도로 관리한다.
key가 같은 여러 개의 query이면 하나의 캐싱으로 관리, key가 다른 여러 개의 query이면 캐싱을 여러 개로 관리한다.
데이터 요청
- data: 서버 요청에 대한 데이터
- isLoadig: 캐시가 없는 상태에서의 데이터 요청 중인 상태 (true/false)
- isFetching: 캐시의 유무와 상관없이 데이터 요청 중인 상태 (true/false)
- isError: 서버 요청 실패에 대한 상태 (true/false)
- error: 서버 요청 실패 (object)
옵션
- cacheTime: 언마운트된 후 어느 시점까지 메모리에 데이터를 저장해 캐싱할 것인지를 결정.
- staleTime: 쿼리가 fresh 상태에서 stale 상태로 전환되는 시간.
- fresh 상태에서는 컴포넌트가 마운트, 업데이트되어도 재요청을 보내지 않으므로 API 요청 횟수를 줄일 수 있다. 보통 쉽게 변하지 않는 컴포넌트에 한해 staleTime을 지정한다.
- refetchOnMount: 컴포넌트 마운트 시 새로운 데이터 패칭.
- refetchOnWindowFocus: 브라우저 클릭 시 새로운 데이터 패칭.
- refetchInterval: 지정한 시간 간격만큼 데이터 패칭. (브라우저에 포커스가 없을 때 실행되지 않음)
- refetchIntervalInBackground: 브라우저에 포커스가 없어도 refetchInterval에서 지정한 시간 간격만큼 데이터 패칭.
- onSuccess: 데이터 패칭 성공
- onError: 데이터 패칭 실패
- enabled: 컴포넌트가 마운트되어도 데이터 패칭 X. useQuery의 반환값 중 refetch를 활용해 데이터 패칠을 할 수 있다.
const { data, isLoading, refetch } = useQuery('super-heroes', fetchSuperHeroes, {
enabled: false,
});
return (
<button onClick={ refetch }>Fetch Button</button>
)
- select: 데이터 패칭 성공 시 원하는 데이터 형식으로 변환 가능.
/*
const { data, isLoading } = useQuery('super-heroes', fetchSuperHeroes);
console.log(data.data)
[
{id: 1, name: 'batman'},
{id: 2, name: 'superman'},
{id: 3, name: 'wonder woman'},
]
*/
const { data, isLoading } = useQuery('super-heroes', fetchSuperHeroes, {
select: (data) => {
return data.data.map(hero => hero.name)
}
});
console.log(data) // ['batman', 'superman', 'wonder woman']
병렬 처리
데이터 패칭이 여러 개 실행되어야 한다면 useQuery를 병렬로 선언하면 된다.
import { useQuery } from 'react-query';
import axios from 'axios';
const fetchSuperHeroes = () => {
return axios.get('http://localhost:4000/superheroes');
};
const fetchFriends = () => {
return axios.get('http://localhost:4000/friends');
};
const ParallelQueries = () => {
const heroes = useQuery('super-heroes', fetchSuperHeroes);
const friends = useQuery('freinds', fetchFriends);
return (
<div>
{heroes.data?.data.map(hero => (
<div key={hero.id}>{hero.name}</div>
)}
{friends.data?.data.map(friend => (
<div key={friend.id}>{friend.name}</div>
)}
</div>
);
};
export default ParallelQueries;
하지만 쿼리의 수가 많아질 경우 변수를 다 기억해야하는 단점이 생기고, 모든 쿼리에 대한 로딩, 성공, 실패 처리를 해줘야 하므로 불편하다.
그럴 때는 useQueries를 사용하면 된다.
const results = useQueries([
{
queryKey: ["super-hero"],
queryFn: () => fetchSuperHeroes()
},
{
queryKey: ["freinds"],
queryFn: () => fetchFriends()
}
]);
동기적 실행
코드가 동기적으로 수행되어야 할 경우, enabled 속성을 이용하면 된다.
const fetchUserByEmail = (email) => {
return axios.get(`http://localhost:4000/users/${email}`);
};
const fetchCoursesByChannelId = (channelId) => {
return axios.get(`http://localhost:4000/channels/${channelId}`);
};
const DependentQueries = ({ email }) => {
const { data: user } = useQuery(['user', email], () => fetchUserByEmail(email));
const channelId = user?.data.channelId;
//❗️이중 부정을 통해서 channelId가 true -> useQuery 실행, false -> 실행 X
useQuery(['courses', channelId], () => fetchCoursesByChannelId(channelId), {
enabled: !!channelId,
});
return <div>DependentQueries</div>;
};
export default DependentQueries;
참고
반응형
'Education > Wanted Pre-Onboarding FE Course' 카테고리의 다른 글
[TIL] 22.05.22 프리온보딩 Day 14 (0) | 2022.05.22 |
---|---|
[TIL] 22.05.19 프리온보딩 Day 13 - Redux Toolkit (0) | 2022.05.20 |
[TIL] 22.05.17 프리온보딩 Day 11 (0) | 2022.05.18 |
[TIL] 22.05.12 프리온보딩 Day 8 - Infinite scroll(intersection-observer) (0) | 2022.05.13 |
[TIL] 22.05.11 프리온보딩 Day 7 (0) | 2022.05.12 |