React

[React] react-query (2) - Queries, Mutations, Query Invalidation

zubetcha 2022. 3. 15. 22:57

 

 

Defaults

cached data

- 더 이상 useQuery, useInfiniteQuery의 활성 인스턴스가 없는 쿼리 결과 또는 쿼리 옵저버들은 inactive 상태가 되고, 나중에 다시 사 용될 때 캐시에 남아 있게 된다.

- 기본적으로 inactive한 쿼리는 5분 후에 GC 처리 된다. 즉 default cacheTime이 5분이라는 의미로, 변경하고 싶다면 cacheTime 옵션에 변경하고자 하는 시간을 설정하면 된다.

- default cacheTime: 5분 (1000 * 60 * 5 ms)

 

stale data

- useQuery 또는 useInfiniteQuery 훅을 사용해서 캐시된 데이터는 기본적으로 stale한 데이터로 취급한다.

- 위와 같은 default option은 전체(global) 쿼리 또는 하나의 쿼리에 대해서 staleTime 옵션을 설정하면 변경할 수 있으며, staleTime이 길다는 건 그 시간 만큼 refetch를 하지 않는다는 것을 의미한다.

- staleTime 기본값은 0ms이다.

- default staleTime: 0

 

refetch

-Stale 쿼리는 아래와 같은 상황에서 자동으로 refetch된다.

 

1) 쿼리의 새로운 인스턴스가 마운트됐을 때

2) 윈도우가 refocused 됐을 때

3) 네트워크가 재연결됐을 때

4) refetch 간격에 대한 옵션이 별도로 설정되어 있을 때

 

위와 같은 refetch를 제어하고 싶다면 각각 refetchOnMount, refetchOnWindowFocus, refetchOnReconnect, refetchInterval 옵션을 변경하면 된다.

 

retry

- 실패한 쿼리는 에러가 캡쳐되고 화면에 표시되기 전에 exponential backoff 지연을 발생시키면서 자동으로 3번까지 다시 시도한다. 

- 기본으로 설정되어 있는 재시도 횟수를 변경하고 싶다면 retry 옵션을, exponential backoff 지연과 관련된 기능을 변경하고 싶다면 retryDelay 옵션을 변경하면 된다.

 

3 core concept

Queries

Query는 서버의 데이터를 받아 오기 위해 사용하며, 공식 문서에서는 만약 서버에 있는 데이터를 수정하거나 삭제하는 게 목적이라면 Queries가 아닌 Mutations을 사용하는 것을 추천하고 있다.

 

 

- 컴포넌트나 커스텀 훅에 쿼리(서버에서 받아 온 데이터)를 구독하고 싶다면 해당 쿼리만을 위한 유니크한 쿼리 키와 Promise를 반환하는 함수와 함께 useQuery 훅을 사용하면 됨

- 유니크한 쿼리 키는 데이터를 refetch하고, cache하고, 어플리케이션 전체에 쿼리를 공유하는 데 사용됨

 

example

import { useQuery } from 'react-query'

function App() {
  const info = useQuery('todos', fetchTodoList)
}

 

위의 예시에서 useQuery 훅의 첫 번째 인자인 'todos'는 unique query key 이며, fetchTodoList는 Promise를 반환하는 fetch 함수임

 

state

- isLoading(boolean) or status === 'loading' : 쿼리에 아직 받아 온 데이터가 없으며 받아 오는 중인 상태

- isError(boolean) or status === 'error' : 쿼리가 에러를 마주침

- isSuccess(boolean) or status === 'success' : 데이터를 받아 오는 데 성공했으며 받아 온 데이터를 사용할 수 있는 상태

- isIdle(boolean) or status === 'idle' : 쿼리가 불가능한 상태

 

property

- error : 만약 현재 쿼리의 상태가 isError(status === 'error')라면, error 프로퍼티를 통해 error를 활용할 수 있음

- data : 만약 현재 쿼리의 상태가 isSuccess(status === 'success')라면, data 프로퍼티를 통해 data를 활용할 수 있음

- isFetching : background refetch를 포함하여 쿼리가 데이터를 받아 오고 있는 상태라면 어느 때이든 isFetching 프로퍼티의 값은 true

 

function Todos() {
  const { isLoading, isError, status, data, error } = useQuery('todos', fetchTodoList)

  // isLoading를 status === 'loading'으로 대체할 수 있다.
  if (isLoading) {
    return <span>Loading...</span>
  }

  // isError를 status === 'error'로 대체할 수 있다.
  if (isError) {
    return <span>Error: {error.message}</span>
  }

  // 여기서 isSuccess의 값이 true 라는 것 또는 status === 'success'라는 것을 추정할 수 있다.
  // 또한 else 로직도 사용할 수 있다.
  return (
    <ul>
      {data.map(todo => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  )
}

 

throwing error

react-query가 에러가 발생했는지 판명할 수 있으려면 반드시 query function이 에러를 뱉어내야 한다. axios나 graphql-request 같은 라이브러리들은 HTTP 통신에 실패한 경우 자동으로 에러를 던지지만 fetch API 등 자동으로 에러를 던지지 않는 것도 있다. 이런 경우에는 직접 에러를 던지도록 추가로 작성해야 한다. 아래의 예시는 공식문서에 나와 있는 직접 에러를 던지도록 하는 간단한 작성 방법이다.

 

useQuery(['todos', todoId], async () => {
  const response = await fetch('/todos/' + todoId)
  if (!response.ok) {
    throw new Error('Network response was not ok')
  }
  return response.json()
})

 

파라미터를 객체로 나타내기

useQuery의 파라미터에 들어가는 queryKey, query function, config를 아래와 같이 key-value 형태의 객체로도 작성할 수 있다.

 

import { useQuery } from 'react-query'
 
useQuery({
  queryKey: ['todo', 7],
  queryFn: fetchTodo,
  ...config,
})

 

동적으로 여러 개의 useQuery 사용하기

 

만약 실행시켜야 하는 쿼리의 개수가 렌더링 할 때마다 동적으로 변한다면 useQueries 훅을 사용하면 된다. useQueries는 쿼리 options의 객체를 요소로 담고 있는 배열을 파라미터로 받아서 각 쿼리의 결과를 요소로 담고 있는 배열을 반환한다.

 

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

 

Mutations

query가 서버의 데이터를 조회하는 데 사용된다면, mutations는 서버에 데이터를 생성하거나, 기존에 있는 데이터를 변경하거나 삭제할 때 사용한다. 사용 방법은 useMutation 훅을 사용하는 것이다.

 

state

mutation은 다음의 네 가지 상태 중 하나의 상태를 가진다.

 

- isIdle or status === 'idle'

- isLoading or startus === 'loading'

- isError or status === 'error'

- isSuccess or status === 'success'

 

property

- error

- data

 

⚠️

mutate 함수는 비동기 함수

 

 

 

Query Invalidation