記錄

React-Query 본문

FRONTEND STUDY/React

React-Query

prts 2023. 2. 27. 17:42

React-query?

- 기존의 Redux, Recoil 등의 전역 상태관리 라이브러리를 사용하여 비동기 데이터를 처리하는 방식은 client state를 처리하는 데는 효과적이나 server state를 처리하는데는 효과적이지 못함

- 프로젝트 크기가 커질수록 api 호출 후 전역 상태를 업데이트해줘야 하는 상황마다 작성해야 하는 코드가 많아지고, 비동기 데이터 처리 로직으로 인해 store가 과도하게 비대해지는 문제점이 있음

1.api 호출 후 응답 데이터 > server state
2.ui 개발 위한 데이터(theme 등) > client state

 

=> 전역상태관리 라이브러리가 본연의 역할인 전역으로 관리해야 할 데이터(Global State)를 관리하는 데 집중할 수 있도록 Server 데이터와 Client 데이터를 분리시켜줌

React Query의 특징

  • Caching 지원.
  • 동일한 데이터에 대한 중복 요청을 제거하고 한 번만 요청하도록 함. 
  • "out of date" 상태의 데이터를 파악하고 updating 지원.
  • Pagination 및 Lazy Loading 성능 최적화.
  • Server State의 메모리 관리 및 garbage collection 지원.
  • React Hooks와 유사한 인터페이스 제공.

staleTime, cacheTime 개념

staleTime

- 신선하지 않은 데이터 = stale(상한 상태)

- 실제 서버에서 갖고 있는 데이터와 클라이언트가 갖고 있는 데이터가 서로 일치하지 않을 가능성이 존재하는 데이터

- 브라우저나 특정 Element에 포커스가 맞춰지면 API를 호출하거나, 일정 시간이 지난 뒤에는 데이터가 상했을 것이라 추측하고 다시 요청하는 등 다양한 방법을 이용해서 데이터를 갱신

- 데이터 fetch(fresh 상태) => staleTime(기본값 0) 이후 stale 상태로 변경

- 데이터가 한번 fetch 되고 나서 staleTime이 지나지 않았다면 unmount 후 mount 되어도 fetch가 일어나지 않음

 

cacheTime : 데이터가 inactive 상태일 때 캐싱된 상태로 남아있는 시간
- unmount되면 데이터는 inactive상태로 변경되고, 일정 시간 지나면 캐시를 삭제함

(staleTime infinity로 설정해도 상관없이 삭제되고, 무조건 inactive된 시점 기준으로 cacheTime만큼 유지됨)

=> useQuery로 패치한 결과를 캐시해두고 있는데, 이걸 신선하지 않다고 보기 때문에 refetch할 수 있음

설치

$ npm i react-query
$ yarn add react-query
 
//react query v4 부터는 tanstack query 로 라이브러리명이 변경
$ npm i @tanstack/react-query
$ yarn add @tanstack/react-query

main.tsx 설정

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { QueryClient, QueryClientProvider } from "react-query";
import "./index.css";

//클라이언트 생성
const queryClient = new QueryClient();

ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
  <React.StrictMode>
  //비동기 요청 처리 위한 Context Provider -> QueryClientProvider로 감싸서 사용
    <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>
  </React.StrictMode>
);

React Query 사용법

useQuery

- 서버에서 데이터를 가져오기(get) 위해 사용하는 hook

- refetching, caching, 전역 사용 위해 유니크한 key(Query Key) 필요

- 중복된 API 호출을 Query Key를 이용하여 구분하고 중복되는 요청에 대해서는 캐시에 저장해 둔 데이터를 활용하여 네트워크 요청을 최소화

- 데이터 사용할 때 필요한 중요한 상태들을 보유하고 있음

 

isLoading status === 'loading' 쿼리 로딩 상태(캐싱된 데이터 없을 때 fetch 중에 true)
isSuccess status === 'success' 쿼리 사용 가능 상태
isIdle status === 'idle' 쿼리를 사용할 수 없는 상태(disabled)
isError status === 'error' 쿼리에 에러가 발생한 상태
error 쿼리가 isError 상태인 경우 에러 정보 확인을 위해 사용하는 프로퍼티
data 쿼리가 isSucess 상태인 경우 데이터 사용을 위해 사용하는 프로퍼티
isFetching 쿼리의 fetching/refetching 여부에 대한 boolean 값
(데이터가 fetch될 때 true, 캐싱 데이터 이어서 백그라운드에서 fetch 되더라도 true)
//사용 예시
const fetchData = async () => {
    const res = await axios.get('https://62debc799c47ff309e7aeb36.mockapi.io/api/tempuser');
    return res.data;
}

const queryfetch = useQuery(
	['temp_query'], //queryKey: 유니크한 키값
    // tanstack query의 경우 key값 하나여도 배열로 선언해야 함(업데이트)
    fetchData, //queryFunction(queryFn): 비동기 API 함수, promise를 반환
    {
    //options : 쿼리에 사용할 옵션값
})
function Todos() {
   const { isLoading, isError, data, error } = useQuery('todos', fetchTodoList)
   if (isLoading) {
     return <span>Loading...</span>
   }
   if (isError) {
     return <span>Error: {error.message}</span>
   }
   // `isSuccess === true`
   return (
     <ul>
       {data.map(todo => (
         <li key={todo.id}>{todo.title}</li>
       ))}
     </ul>
   )
 }

useQueries

- useQuery는 기본적으로 비동기로 동작하기 때문에 컴포넌트 내에 여러 개의 useQuery가 있을 경우 동시에 실행됨

- useQueries 사용하면 여러 개의 쿼리를 하나로 묶어서 사용할 수 있음

- 쿼리 옵션 객체들의 배열을 받고 배열의 형태로 반환

 function App({ users }) {
   const userQueries = useQueries(
     users.map(user => {
       return {
         queryKey: ['user', user.id],
         queryFn: () => fetchUserById(user.id),
       }
     })
   )
 }

Mutation

- 전역적으로 사용됨(Create, Update, Delete data)

- useMutation hook 사용

function App(){
  const mutation = useMutation(newTodo => {
    return axios.post('/todos', newTodo)
  })

  return (
    <div>
      {mutation.isLoading ? (Adding todo...) : (
         <>
            {mutation.isError ? (
            <div>An error occurred: {mutation.error.message} </div>
	) : null}
	{mutation.isSuccess ? <div>Todo added!</div>: null}
	<button onClick={()=>{mutation.mutate({id:new Date(), title: 'Do Laundry'})}}>
	  Create Todo
	</button>
         </>
      )}
    </div>
  )
}

Query Invalidation

- invalidateQueries를 사용해 쿼리 무효화

- 쿼리를 stale하다고 마크해두고 잠재적으로 refetch

queryClient.invalidateQueries() //아무 인자 남기지 않으면 캐시된 모든 쿼리에 대해 invalidate 처리
queryClient.invalidateQueries('todos') //문자열을 가지고 시작하는 값들에 대해 invalidate 처리

default 설정하기

import { QueryClient } form 'react-query'

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: Infinity, 
    }
  }
})

참고자료

https://tech.osci.kr/2022/07/13/react-query/

https://www.dgmunit1.com/blog/setting/next-tanstack-query#_apptsx-%EC%84%A4%EC%A0%95

https://freestrokes.tistory.com/170

https://velog.io/@yrnana/React-Query%EC%97%90%EC%84%9C-staleTime%EA%B3%BC-cacheTime%EC%9D%98-%EC%B0%A8%EC%9D%B4

 

 

 

'FRONTEND STUDY > React' 카테고리의 다른 글

JSON-SERVER  (0) 2023.02.20
[React 스터디] SPA, Routing  (0) 2023.01.09
[React 스터디 - 6] 리액트 CSS 적용 방법  (0) 2022.12.25
[React 스터디-5] List, Key  (0) 2022.12.18
[React Study - 5] Life cycle, useEffect()  (0) 2022.12.11
Comments