React-Query 공식문서 살펴보기

nevermind·2023년 1월 18일
0

React-Query

목록 보기
1/1
  • 블로그에도 잘못된 정보들이 있기에 공식문서를 보는 습관을 들이고자 정리해보기로 했다!

Overview

  • 리액트 쿼리는 누락된 데이터 가져오는 라이브러리로 설명되지만, 리액트 애플리케이션에서 서버 가져오기, 캐싱, 동기화 및 업데이트를 쉽게 만들어준다

  • 대부분의 상태 관리 라이브러리는 상태 작업에 적합하지만 비동기 또는 서버 상태에는 적합하지 않다. 그 이유는 서버 상태가 완전히 다르기 때문이다.

    • 내가 통제하거나 소유하지 않는 위치에 원격으로 유지되기도 함
    • 가져오기 및 업데이트를 위한 비동기 API가 필요하다
    • 다른 사람들과 공유되는 것으로 사용자가 모르는 사이에 변경될 수 있다
    • 신경쓰지 않으면 잠재적으로 'out of date'가 될 가능성이 있다
  • 애플리케이션에서 서버 상태의 특성을 파악하면 더 많은 문제가 발생한다

    • 캐싱
    • 동일한 데이터 중복 요청
    • 백그라운드에서 오래된 데이터 시점 알기
    • 데이터 업데이트의 신속한 반영
    • 페이지네이션 및 지연 로딩 데이터의 성능 최적화
  • 위의 문제들을 리액트 쿼리는 해결할 수 있는 서버 상태 관리 라이브러리 중 하나이다

  • 리액트 쿼리를 씀으로써 보일러플레이트를 제거하고 빠르고 잠재적으로 대역폭을 절약하고 메모리 성능을 높이는데 도움이 된다

Installation

  • $ npm i react-query 또는 $ yarn add react-query

Quick Start

  • 리액트 쿼리의 3가지 핵심을 간략하게 보여주겠다

Queries

A query is a declarative dependency on an asynchronous source of data that is tied to a unique key. A query can be used with any Promise based method (including GET and POST methods) to fetch data from a server. If your method modifies data on the server, we recommend using Mutations instead.
쿼리는 고유 키에 연결된 데이터의 비동기 소스에 선언적 종속성을 가진다. 쿼리는 Promise기반 메서드(GET 및 POST)와 함께 사용하여 서버에서 데이터를 가져올 수 있다. 메서드가 수정하는 경우에는 Mutation을 사용한다

  • 컴포넌트 또는 커스텀 훅에 쿼리를 구독하고 사용하고 싶다면 useQuery훅을 호출한다
  • 데이터 처리 성공하거나 에러가 반환된다
 import { useQuery } from 'react-query'

function App() {
  const info = useQuery('todos', fetchTodoList)
}
  • unique key는 애플리케이션 전체에서 쿼리를 다시 가져오고, 캐싱하고, 공유하기 위해 내부에서 사용된다

  • useQuery에서 반환된 쿼리 결과에는 템플릿 작성 및 기타 쿼리에 필요한 모든 정보가 포함되어있다.

  • result object에는 주어진 순간에 다음 상태 중 하나만 있을 수 있다. (주요상태)

    • ⭐ isLoading : 현재 데이터 없고 연결중
    • ⭐ isError : 에러
    • ⭐ isSuccess : 데이터가 성공적, 사용가능함
    • isIdle : 쿼리가 현재는 비활성화
  • 그 이외에도 상태에 의존하여 사용가능한 정보들이 있다

    • data : 쿼리가 success하다면 data를 사용할 수 있다
    • isFetching: 어떤 상태에서든 쿼리가 언제든지 가져와지는 경우, isFetching이 true가 된다

Mutations

  • Queries와 달리 Mutations는 일반적으로 데이터를 create,update,delete시 사용
  • useMutation 훅을 보낸다
 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>
   )
 }
  • mutation도 주어진 순간에 하나만 나타낼 수 있다
    • isIdle : mutation이 현재 비활성화상태이거나 신규/재설정 상태
    • isLoading : 로딩중
      - isError : 에러 발생
      - isSuccess : mutation 성공, 사용 가능
  • 위와 동일하게 그 외에도 mutation 상태에 의존하여 사용가능한 정보들이 있다
    • error
    • data
  • onSuccess, invalidateQueries method, setQueryData method를 같이 사용하면 mutation은 강력한 도구가 된다

Resetting Mutation State

  • mutation요청의 오류나 데이터를 지워야하는 경우, reset 기능을 통해 처리할 수 있다.
{mutation.error && (
         <h5 onClick={() => mutation.reset()}>{mutation.error}</h5>
       )}

Mutation Side Effects

  • useMutation은 mutation 라이프사이클동안 빠르고 쉽게 사이드 이펙트를 적용하도록 도와주는 옵션들이 있다. 이것들은 mutation 그리고 업데이트 이후 쿼리를 무효화하고 다시 가져오는데 유용하다
 useMutation(addTodo, {
   onMutate: variables => {
     // A mutation is about to happen!
 
     // Optionally return a context containing data to use when for example rolling back
     return { id: 1 }
   },
   onError: (error, variables, context) => {
     // An error happened!
     console.log(`rolling back optimistic update with id ${context.id}`)
   },
   onSuccess: (data, variables, context) => {
     // Boom baby!
   },
   onSettled: (data, error, variables, context) => {
     // Error or success... doesn't matter!
   },
 })

Consecutive mutations

  • Success, onError 및 onSettled 콜백을 연속적으로 mutation할 때 약간의 차이가 있다
  • mutate 함수에 전달되면 component가 여전히 mounted 되어 있는 경우 한번만 실행된다. 이는 mutate 함수가 호출될 때마다 mutation 관찰자가 제거되고 다시 구독되기 때문이다.
  • 반대로 useMutation은 각 mutate호출에 대해 실행한다.
 useMutation(addTodo, {
   onSuccess: (data, error, variables, context) => {
     // Will be called 3 times
   },
 })
 
 ['Todo 1', 'Todo 2', 'Todo 3'].forEach((todo) => {
   mutate(todo, {
     onSuccess: (data, error, variables, context) => {
       // Will execute only once, for the last mutation (Todo 3),
       // regardless which mutation resolves first 
     },
   })
 })

Promises

  • mutate 대신 mutateAsync를 사용하여 try, catch문 사용으로 오류 감지할 수 있다
 const mutation = useMutation(addTodo)
 
 try {
   const todo = await mutation.mutateAsync(todo)
   console.log(todo)
 } catch (error) {
   console.error(error)
 } finally {
   console.log('done')
 }
Retry

Retry

  • 기본적으로 React Query는 오류 발생 시 변형을 재시도하지 않지만 재시도 옵션을 사용하면 가능
  • mutation이 오프라인 기기여서 실패한다면 재연결할 수 있다
 const mutation = useMutation(addTodo, {
   retry: 3,
 })

Persist mutations

  • 필요한 경우 mutation 스토리지에 유지하고 나중에 재개할 수 있다
  • 이 기능은 hydration으로 가능
 const queryClient = new QueryClient()
 
 // Define the "addTodo" mutation
 queryClient.setMutationDefaults('addTodo', {
   mutationFn: addTodo,
   onMutate: async (variables) => {
     // Cancel current queries for the todos list
     await queryClient.cancelQueries('todos')
 
     // Create optimistic todo
     const optimisticTodo = { id: uuid(), title: variables.title }
 
     // Add optimistic todo to todos list
     queryClient.setQueryData('todos', old => [...old, optimisticTodo])
 
     // Return context with the optimistic todo
     return { optimisticTodo }
   },
   onSuccess: (result, variables, context) => {
     // Replace optimistic todo in the todos list with the result
     queryClient.setQueryData('todos', old => old.map(todo => todo.id === context.optimisticTodo.id ? result : todo))
   },
   onError: (error, variables, context) => {
     // Remove optimistic todo from the todos list
     queryClient.setQueryData('todos', old => old.filter(todo => todo.id !== context.optimisticTodo.id))
   },
   retry: 3,
 })
 
 // Start mutation in some component:
 const mutation = useMutation('addTodo')
 mutation.mutate({ title: 'title' })
 
 // If the mutation has been paused because the device is for example offline,
 // Then the paused mutation can be dehydrated when the application quits:
 const state = dehydrate(queryClient)
 
 // The mutation can then be hydrated again when the application is started:
 hydrate(queryClient, state)
 
 // Resume the paused mutations: 
//일시중지된 mutation을 재개
 queryClient.resumePausedMutations()

Query Invalidation

  • invalidateQueries메서드를 통해 QueryClient에 오래된 쿼리를 표시하고 잠재적으로 가져 올 수 있다
 // Invalidate every query in the cache
 queryClient.invalidateQueries()
 // Invalidate every query with a key that starts with `todos`
 queryClient.invalidateQueries('todos')
  • 쿼리가 invalidateQueries로 무효화되면 두가지 일이 발생한다

    • stale로 표시(오래된것), stale 상태는 useQuery 또는 관련 훅에서 사용되는 staleTime 구성을 재정의한다.
    • useQuery 또는 관련 훅을 통해 렌더링되는 경우 백그라운드에서도 다시 가져온다

    Query Matching with invalidateQueries

  • invalidateQueries 및 removeQueries와 같은 API를 사용하는 경우 여러 쿼리를 일치시키거나 실제로 특정정확한 쿼리를 일치시킬 수 있다.

    import { useQuery, useQueryClient } from 'react-query'
    
    // Get QueryClient from the context
    const queryClient = useQueryClient()
    
    queryClient.invalidateQueries('todos')
    
    // Both queries below will be invalidated
    const todoListQuery = useQuery('todos', fetchTodoList)
    const todoListQuery = useQuery(['todos', { page: 1 }], fetchTodoList)
- 특정 키를 invalidateQueries메서드에 전달하여 특정 변수가 있는 쿼리를 무효화할 수 있다
```jsx
 queryClient.invalidateQueries(['todos', { type: 'done' }])
 
 // The query below will be invalidated
 const todoListQuery = useQuery(['todos', { type: 'done' }], fetchTodoList)
 
 // However, the following query below will NOT be invalidated
 const todoListQuery = useQuery('todos', fetchTodoList)
  • invalidateQueries API는 유연하다. 더 이상 변수나 하위 키가 없는 todos 쿼리만 무효화하려는 경우에도 exact: true 옵션을 invalidateQueries 메서드에 전달 가능
 queryClient.invalidateQueries('todos', { exact: true })
 
 // The query below will be invalidated
 const todoListQuery = useQuery(['todos'], fetchTodoList)
 
 // However, the following query below will NOT be invalidated
 const todoListQuery = useQuery(['todos', { type: 'done' }], fetchTodoList)
  • 더 많이 세분화하고 싶다면, invalidateQueries 메소드에 조건자 함수를 전달할 수 있다. 이 함수는 해당 쿼리를 무효화할지 여부에 대해 true || false로 반환할 수 있다.
queryClient.invalidateQueries({
   predicate: query =>
     query.queryKey[0] === 'todos' && query.queryKey[1]?.version >= 10,
 })
 
 // The query below will be invalidated
 const todoListQuery = useQuery(['todos', { version: 20 }], fetchTodoList)
 
 // The query below will be invalidated
 const todoListQuery = useQuery(['todos', { version: 10 }], fetchTodoList)
 
 // However, the following query below will NOT be invalidated
 const todoListQuery = useQuery(['todos', { version: 5 }], fetchTodoList)

출처 : https://react-query-v3.tanstack.com/overview

profile
winwin

0개의 댓글