참고 : Client State와 Server State
Client State
- 웹 브라우저 세션과 관련된 데이터
- ex) 언어 선택, 테마 선택
Server State
- 서버에 저장된 데이터. 클라이언트에 표시하기 위해 필요
- ex) 블로그 게시물 데이터
Server State(서버 상태)를 관리하는 리액트 라이브러리
서버 데이터의 캐시를 관리 (해당 캐시의 데이터를 설정한 바에 따라 유지 관리)
서버 데이터가 필요할 때, fetch
, axios
등을 이용해 바로 서버에 요청하지 않고, 일단 React Query cache를 요청한다.
서버의 새 데이터로 캐시를 업데이트하는 시기는 사용자가 지정해줘야 한다.
캐시 업데이트 시점을 지정해주는 여러 방법들..
1)
invalidate
: 데이터를 무효화한다 refetch가 이루어지도록 한다.
2) refetch가 일어나는 조건을 지정한다 (e.g. window focus 등)
3)staleTime
을 지정한다.
App.jsx
import { Posts } from './Posts'; import './App.css'; import { QueryClient, QueryClientProvider } from 'react-query'; // 불러오기 const queryClient = new QueryClient(); // queryClient 생성 function App() { return ( <QueryClientProvider client={queryClient}> // QueryClientProvider 적용 <div className="App"> <h1>Blog Posts</h1> <Posts /> </div> <ReactQueryDevtools /> // 넣기 </QueryClientProvider> ); } export default App;
Post.jsx
import { useState } from 'react'; import { useQuery } from 'react-query'; import { PostDetail } from './PostDetail'; const maxPostPage = 10; async function fetchPosts() { // 쿼리 함수 const response = await fetch( 'https://jsonplaceholder.typicode.com/posts?_limit=10&_page=0' ); return response.json(); } export function Posts() { const [currentPage, setCurrentPage] = useState(0); const [selectedPost, setSelectedPost] = useState(null); // replace with useQuery const { data } = useQuery('posts', fetchPosts ); // useQuery로 쿼리 생성 return ( <> <ul> {data.map((post) => ( // 받아온 data 사용 <li ...
{JSON} Placeholder
- CRUD 가능한 무료 가상 REST API 서버
- 설치 필요 X. 호출만 하면 된다.
data
외에 isLoading
, isError
, error
등을 받아올 수 있다. isLoading
, isError
는 boolean 값.Post.jsx
... export function Posts() { const { data, isError, error, isLoading } = useQuery('posts', fetchPosts); if (isLoading) return <h3>Loading...</h3>; // Loading 처리 if (isError) // Error처리 return ( <> <h3>Oops, something went wrong!</h3> <p>{error.toString()}</p> // error메시지 보여주기 </> ); return ( <> <ul> {data.map((post) => ( <li ...
isFetching vs. isLoading
isFetching
- 데이터를 fetch하는 비동기 쿼리 (함수)가 아직 처리중일 때.
- 캐싱 데이터가 있어서 백그라운드에서 fetch 되더라도
true
isLoading
- isFetching + 캐시된 데이터가 없음 (= 이 쿼리를 만든 적이 없음. 처음 만드는 것)
App.jsx
... import { QueryClient, QueryClientProvider } from 'react-query'; import { ReactQueryDevtools } from 'react-query/devtools'; // 불러오기 const queryClient = new QueryClient(); function App() { return ( <QueryClientProvider client={queryClient}> <div className="App"> <h1>Blog Posts</h1> <Posts /> </div> <ReactQueryDevtools /> // 넣기 </QueryClientProvider> ); } export default App;
React Query의 라이프 사이클
- A 쿼리 인스턴스가 mount 됨
- 네트워크에서 데이터 fetch 하고 A라는 query key로 캐싱함
- 이 데이터는
fresh
상태에서staleTime
(기본값 0) 이후stale
상태로 변경됨- A 쿼리 인스턴스가 unmount 됨 = 데이터가
inactive
상태로 변경됨- 캐시는
cacheTime
(기본값 5분) 만큼 유지되다가 가비지 콜렉터로 수집됨- 만일
cacheTime
이 지나기 전에 A 쿼리 인스턴스가 새롭게 mount되면, fetch가 실행되고 fresh한 값을 가져오는 동안 캐시 데이터를 보여줌
fresh
-> stale
상태로 변경되는데 걸리는 시간stale
상태일 경우에만 실행됨.fresh
상태일 때는 refetch 일어나지 않음)😀
refetchOnMount
설정값이 true(기본값)라면 mount될 때 refetch가 일어나지만...
만일 staleTime을 길게 설정해서 아직 fresh 상태인 채로 unmount 됐다가 다시 mount 될 때까지 staleTime이 끝나지 않았다면?
👉🏽 refetch 되지 않는다! (refetch는 stale 상태일 경우에만 일어난다)
inactive
되고 나서 캐싱된 상태로 남아있는 시간useQuery
가 마지막으로 실행된 시점으로부터 계산 (...?)stale
상태가 되어서 쿼리 인스턴스가 unmount 되면 데이터는 inactive
상태로 변경되며, 이후 캐시는 cacheTime
만큼 유지된다.inactive
된 시점을 기준으로 cachTime
계산이 시작된다)cacheTime
이 지나면 가비지 콜렉터로 수집된다. (이후에는 클라이언트에서 데이터를 보여줄 수 없다. 빈 화면으로 보여짐)cacheTime
이 지나기 전에 쿼리 인스턴스가 다시 마운트 되면, 데이터를 fetch하는 동안 캐시 데이터를 보여준다.👉🏽 보통은 빈 화면을 보여주는 것보다 약간 오래된 데이터(stale 된 후 캐싱 상태의 데이터)를 표시하는 것이 낫지만
👉🏽 조금이라도 오래된 데이터를 보여주면 안되는 경우에는cacheTime
을 0으로 설정해준다.
Post.jsx
... const { data, isError, error, isLoading } = useQuery('posts', fetchPosts, { staleTime: 2000, cacheTime: 1500000 });
참고
[react-query를 이해하는 몇가지 지식과 질문들]
refetching을 왜 하는가? => 지금 가지고 있는 정보가 fresh하지 않고 stale하니까
refetching 조건을 만족해도 fresh하다면 refetching하지 않는가? => 그렇다.
staleTime이 0이라면 어떻게 되는가? => 곧바로 stale이 되므로 refetching이 요구되는 상황이 오면 무조건 refetching하게 된다.
언제 데이터가 cache되는가? => query를 보내고 데이터를 받아오자마자 cache된다. 그러나 cacheTime은 이 때 발동하지 않는다.
그렇다면 cacheTime은 언제부터 시작인가? => unmount된 시점부터. 즉, inactive된 시점부터 시작한다.
cacheTime이 지나기 전에 다시 쿼리가 발동되면 어떻게 되는가? => cache된 값을 사용하고 background에서 다시 fetching된다.
cacheTime이 지나면 어떻게 되는가? => 메모리에 존재하는 데이터가 GC에 의해 삭제. 따라서 다시 active되면 hard loading한다.
cacheTime이 0이라면 어떻게 되는가? => 매번 GC 당하므로 매번 hard loading을 하게 된다.