React Query¶
React Query - библиотека для получения, кэширования, синхронизации и обновления "серверного" состояния в React-приложениях.
Установка¶
1 2 3 |
|
Пример¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
|
Быстрый старт¶
Ключевыми концепциями React Query
являются:
- Запросы (queries)
- Мутации (mutations)
- Инвалидация (аннулирование, признание недействительным) запроса (query invalidation), его кэша
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
|
Инструменты разработчика¶
По умолчанию инструменты разработчика не включаются в производственную сборку (когда process.env.NODE_ENV === 'production'
).
Существует два режима добавления инструментов в приложение: плавающий (floating) и встроенный (inline).
Плавающий режим¶
1 2 3 4 5 6 7 8 |
|
Настройки¶
initialIsOpen
- еслиtrue
, то панель инструментов по умолчанию будет открытаpanelProps
- используется для добавления пропов к панели, например,className
,style
и т.д.closeButtonProps
- используется для добавления пропов к кнопке закрытия панелиtoggleButtonProps
- используется для добавления пропов к кнопке переключенияposition
: "top-left" | "top-right" | "bottom-left" | "bottom-right" - положение логотипаReact Query
для открытия/закрытия панели
Встроенный режим¶
1 2 3 4 5 6 7 8 9 10 11 |
|
Важные настройки по умолчанию¶
- Экземпляры запроса(query instances), созданные с помощью
useQuery
илиuseInfiniteQuery
по умолчанию считают кэшированные данные устаревшими (stale) - Устаревшие запросы автоматически повторно выполняются в фоновом режиме (background) в следующих случаях:
- Монтирование нового экземпляра запроса
- Переключения окна в браузере
- Повторное подключение к сети
- Когда задан интервал выполнения повторного запроса (refetch interval)
- Результаты запроса, не имеющие активных экземпляров
useQuery
,useInfiniteQuery
или наблюдателей (observers) помечаются как "неактивные" (inactive) и остаются в кэше - По умолчанию запросы, помеченные как неактивные, уничтожаются сборщиком мусора через 5 минут
- Провалившиеся запросы автоматически повторно выполняются 3 раза с экспоненциально увеличивающейся задержкой перед передачей ошибки в UI
- Результаты запросов по умолчанию структурно распределяются для определения того, действительно ли данные изменились, и если это не так, ссылка на данные остается неизменной, что способствует стабилизации данных при использовании
useMemo
иuseCallback
Запросы¶
Основы¶
Запрос - это декларативная зависимость асинхронного источника данных, связанного с уникальным ключом. Запрос может использоваться с любым основанным на промисе методом (включая методы GET
и POST
) получения данных от сервера. Если метод изменяет данные на сервере, то лучше использовать мутации.
Для подписки на запрос в компоненте или пользовательском хуке следует вызвать хук useQuery
со следующими параметрами:
- Уникальный ключ запроса
- Функция, возвращающая промис, который
- разрешается данными (resolve data) или
- выбрасывает исключение (throw error)
1 2 3 4 5 |
|
Уникальный ключ используется для выполнения повторных запросов, кэширования и распределения запросов в приложении.
Результаты запроса, возвращаемые useQuery
, содержат всю необходимую информацию о запросе.
1 |
|
Объект result
содержит несколько важных состояний (states). Запрос может находиться в одном из следующих состояний:
isLoading
илиstatus === 'loading'
- запрос находится на стадии выполнения, данные еще не полученыisError
илиstatus === 'error'
- запрос завершился ошибкойisSuccess
илиstatus === 'success'
- запрос завершился успешно, данные доступныisIdle
илиstatus === 'idle'
- запрос отключен
Кроме того, объект result
содержит следующую информацию:
error
- если запрос находится в состоянииisError
, ошибка доступна через свойствоerror
data
- если запрос находится в состоянииsuccess
, данные доступны через свойствоdata
isFetching
- в любом состоянии, если запрос находится на стадии выполнения (включая фоновый повторный запрос)isFetching
будет иметь значениеtrue
Для большинства запросов имеет смысл сначала проверять состояние isLoading
, затем состояние isError
и, наконец, использовать данные для рендеринга:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Вместо логических значений можно использовать состояние status
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Ключи запроса¶
React Query
осуществляет кэширование запросов на основе ключей. Ключи могут быть любыми уникальными сериализуемыми значениями (строками, массивами, объектами и т.д.).
Строковые ключи¶
При передачи строки в качестве ключа запроса, она преобразуется в массив с единственным элементом. Данный формат может использоваться для:
- Общих ресурсов списка/индекса
- Неиерархических ресурсов
1 2 |
|
Ключи в виде массива¶
Когда для описания данных требуется больше информации, в качестве ключа запроса можно передать массив со строкой и любым количеством сериализуемых объектов. Такой формат может использоваться для:
- Иерархических или вложенных ресурсов
- В этом случае, обычно, передается идентификатор, индекс или другой примитив для определения элемента
- Запросов с дополнительными параметрами
- В этом случае, как правило, передается объект с дополнительными настройками
1 2 3 4 5 6 7 8 9 10 11 |
|
Ключи хешируются детерменировано¶
Это означает, что порядок расположения ключей в объекте не имеет значения:
1 2 3 4 |
|
А порядок расположения ключей в массиве, напротив, имеет значение:
1 2 3 4 |
|
Обратите внимание, что если функция запроса использует переменную, такая переменная должна включаться в ключ запроса:
1 2 3 4 5 |
|
Функции запроса¶
Функция запроса может быть любой функцией, возвращающей промис, который, в свою очередь, должен либо разрешаться данными, либо выбрасывать исключение:
1 2 3 4 5 6 |
|
Обработка ошибок¶
При возникновении ошибки, функция запроса должна выбрасывать исключение, которое сохраняется в состоянии error
запроса:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Переменные функции запроса¶
При необходимости, в функции запроса можно получить доступ к переменным, указанным в ключе запроса:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Использование объекта вместо параметров¶
Для настройки запроса можно использовать объект:
1 2 3 4 5 6 7 |
|
Параллельные запросы¶
"Параллельными" называются запросы, которые выполняются одновременно.
Когда количество запросов остается неизменным, для их параллельного выполнения достаточно указать несколько хуков useQuery
или useInfiniteQuery
:
1 2 3 4 5 6 7 |
|
Когда количество запросов меняется от рендеринга к рендерингу, для их параллельного выполнения следует использовать хук useQueries
. Он принимает массив объектов с настройками запроса и возвращает массив результатов запросов:
1 2 3 4 5 6 7 8 |
|
Зависимые запросы¶
Начало выполнения зависимых (или последовательных) запросов зависит от окончания выполнения предыдущих запросов. Для реализации последовательного выполнения запросов используется настройка enabled
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Индикаторы получения данных в фоновом режиме¶
В большинстве случаев для отображения индикатора загрузки во время получения данных достаточно состояния status === 'loading'
. Но иногда может потребоваться отображать индикатор для запроса, выполняющегося в фоновом режиме. Для этого запросы предоставляют дополнительное логическое значение isFetching
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Для отображения глобального индикатора загрузки при выполнении любого запроса (в том числе, в фоновом режиме) можно использовать хук useIsFetching
:
1 2 3 4 5 6 7 8 9 |
|
Выполнение повторного запроса при фокусировке на окне¶
Если пользователь покидает приложение и возвращается к устаревшим данным, React Query
автоматически запрашивает свежие данные в фоновом режиме. Это поведение можно отключить глобально или в отношении конкретного запроса с помощью настройки refetchOnWindowFocus
.
Глобальное отключение:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Отключение для запроса:
1 2 3 |
|
Отключение/приостановка выполнения запросов¶
Для отключения автоматического выполнения запроса используется настройка enabled
.
При установка значения данной настройки в false
:
- Если запрос имеет кэшированные данные
- Запрос будет инициализирован в состоянии
status === 'success'
илиisSuccess
- Если запрос не имеет таких данных
- Запрос бует запущен в состоянии
status === 'idle'
илиisIdle
- Запрос не будет автоматически выполняться при монтировании
- Запрос не будет автоматически выполняться повторно при монтировании или появлении нового экземпляра
- Запрос будет игнорировать вызовы
invalidateQueries
иrefetchQueries
на клиенте запроса (query client), обычно, приводящие к выполнению повторного запроса - Для ручного выполнения запроса может быть использован метод
refetch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
|
Повторное выполнение провалившихся запросов¶
При провале запроса, автоматически выполняется 3 попытки его повторного выполнения.
Это поведение можно изменить как на глобальном уровне, так и на уровне конкретного запроса:
- Установка
retry: false
отключает повторы - Установка
retry: 6
увеличивает количество повторов до 6 - Установка
retry: true
делает повторы бесконечными - Устанока
retry: (failureCount, error) => {}
позволяет кастомизировать обработку провала
1 2 3 4 5 6 |
|
По умолчанию задержки между повторами составляют 2000, 4000 и 6000 мс. Это поведение можно изменить с помощью настройки retryDelay
.
Запросы для пагинации¶
Для реализации пагинации с помощью React Query
достаточно включить информацию о странице в ключ запроса:
1 |
|
Тем не менее, если запустить данный пример, то обнаружится, что UI перепрыгивает между состояниями success
и loading
, поскольку каждая новая страница расценивается как новый запрос.
Для решения этой проблемы React Query
предоставляет keepPreviousData
. Установка данной настройки в значение true
приводит к следующему:
- Данные последнего успешного запроса остаются доступными во время запроса новых данных
- После получения новых данных старые
data
мягко ими заменяются isPreviousData
позволяет определять, какие данные предоставляются текущим запросом
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
|
keepPreviousData
также работает с хуком useInfiniteQuery
, что позволяет пользователям продолжать просмотр кэшированных данных при изменении ключей запроса.
Бесконечные запросы¶
Для запроса бесконечных списков (например, "загрузить еще" или "бесконечная прокрутка") React Query
предоставляет специальную версию useQuery
- useInfiniteQuery
.
Особенности использования useInfiniteQuery
:
data
- объект, содержащий бесконечные данные:data.pages
- массив полученных страницdata.pageParams
- массив параметров страницы, использованных для запроса страниц- Доступны функции
fetchNextPage
иfetchPreviousPage
- Доступны настройки
getNextPageParam
иgetPreviousPageParam
, позволяющие определить наличие данных для загрузки - Доступно логическое значение
hasNextPage
. Если оно равняетсяtrue
,getNextPageParam
вернет значение, а неundefined
- Доступно логическое значение
hasPreviousPage
. Если оно равняетсяtrue
,getPreviousPageParam
вернет значение, а неundefined
- Доступны логические значения
isFetchingNextPage
иisFetchingPreviousPage
, позволяющие различать состояние фонового обновления и состояние дополнительной загрузки
Примеры¶
Предположим, что у нас имеется API, возвращающий страницы с тремя projects
за раз на основе индекса cursor
, а также сам курсор, который может быть использован для получения следующей группы проектов:
1 2 3 4 5 6 7 8 |
|
Располагая этими сведениями, мы можем реализовать UI "Загрузить еще" посредством:
- Ожидания, пока
useInfiniteQuery
выполнить запрос на получение первой порции данных по умолчанию - Возвращения информации для следующего запроса в
getNextPageParam
- Вызова функции
fetchNextPage
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
|
По умолчанию переменная, возвращаемая из getNextPageParam
, передается в функцию запроса. Пользовательская переменная, переданная в fetchNextPage
, перезаписывает дефолтную:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Двунаправленный бесконечный список можно реализовать с помощью getPreviousPageParam
, fetchPreviousPage
, hasPreviousPage
и isFetchingPreviousPage
:
1 2 3 4 5 6 |
|
Ручное удаление первой страницы:
1 2 3 4 |
|
Ручное удаление значения из конкретной страницы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Заменители данных запроса¶
Данные-заменители (placeholder data) позволяют запросам рендерить частичный ("фейковый") контент до получения настоящих данных в фоновом режиме. Это дает результат, похожий на использование настройки initialData
, но при этом данные не сохраняются в кэше.
Существует два способа помещения данных в кэш для использования в качестве заменителей:
- Декларативный:
- Предоставление запросу
placeholderData
для заполнения пустого кэша - Императивный:
- Предварительное или обычное получение данных с помощью
queryClient
и настройкиplaceholderData
Данные-заменители как значение¶
1 2 3 4 5 |
|
Данные-заменители как функция¶
Если процесс получения данных-заменителей является интенсивным или мы не хотим, чтобы он повторялся при каждом рендеринге, тогда в качестве значения placeholderData
можно мемоизировать значение или передать мемоизированную функцию:
1 2 3 4 5 6 7 8 9 |
|
Данные-заменители из кэша¶
В некоторых случаях может потребоваться использовать в качестве данных-заменителей кэшированные результаты другого запроса. Хорошим примером такого случая является поиск кэшированных данных списка запросов поста в блоге для превью поста и последующее использование таких данных в качестве заменителя для запроса на получение конкретного поста:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Начальные данные запроса¶
Существует несколько способов помещения в кэш начальных данных для запроса:
- Декларативный
- Предоставление запросу
initialData
для заполнения пустого кэша - Императивный
- Предварительное получение данных с помощью
queryClient.prefetchQuery
- Ручное помещение данных в кэш с помощью
queryClient.setQueryData
Использование initialData
для подготовки запроса¶
Если в нашем приложении имеются начальные данные для запроса, мы можем передать их в запрос с помощью настройки initialData
, минуя состояние начальной загрузки.
1 2 3 4 5 |
|
staleTime
и initialDataUpdatedAt
¶
По умолчанию initialData
считаются свежими, как будто они были только что получены. Интерпретация начальных данных зависит от настройки staleTime
(время устаревания):
- Если наблюдатель запроса был настроен с помощью
initialData
и не было указаноstaleTime
(по умолчаниюstaleTime
равняется0
), запрос будет выполнен повторно незамедлительно при монтировании:
1 2 3 4 5 6 |
|
- Если наблюдатель запроса был настроен с помощью
initialData
и было указаноstaleTime
в количестве1000
мс, данные будут считаться свежими на протяжении указанного времени:
1 2 3 4 5 6 7 |
|
- Что если наши начальные данные не совсем свежие? Настройка
initialDataUpdatedAt
позволяет указывать время последнего обновления начальных данных в мс:
1 2 3 4 5 6 7 8 9 |
|
Функция начальных данных¶
Если процесс получения начальных данных для запроса является интенсивным или мы не хотим, чтобы он повторялся при каждом рендеринге, то в качестве значения initialData
можно передать функцию. Эта функция будет выполнена только один раз при инициализации запроса:
1 2 3 4 5 6 7 |
|
Начальные данные из кэша¶
В некоторых случаях в качестве начальных данных запроса можно использовать кэшированные результаты другого запросаю. Хорошим примером такого случая является поиск кэшированных данных запроса списка задач для конкретной задачи и использование этих данных в качестве начальных для запроса на получение конкретной задачи:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Начальные данные из кэша с initialDataUpdatedAt
¶
Для определения того, насколько начальные данные из кэша являются свежими и требуется ли выполнение повторного запроса используется настройка initialDataUpdatedAt
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Условное получение начальных данных из кэша¶
Если мы не хотим использовать старый кэш в качестве начальных данных, то можем использовать метод queryClient.getQueryState
для получения информации, включая state.dataUpdatedAt
, которую можно использовать для определения того, достаточно ли свежими являются кэшированные данные:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Предварительное получение данных¶
Если у нас есть возможность предугадывать желания пользователей на получение определенных данных, мы можем выполнить предварительный запрос на их получение и заблаговременно поместить их в кэш:
1 2 3 4 |
|
- Если данные для этого запроса уже имеются в кэше и не аннулированы, запрос выполнен не будет
- Если указано
staleTime
, например,prefetchQuery('todos', fn, { staleTime: 5000 })
и данные старше, чем значениеstaleTime
, запрос будет выполнен - Если не имеется ни одного экземпляра
useQuery
для предварительного запроса, он будет удален и уничтожен сборщиком мусора по истечении времени, указанного вcacheTime
В качестве альтернативы, когда у нас уже имеются данные для запроса, нам не нужно выполнять предварительный запрос на их получение. Мы можем просто использовать метод setQueryData
клиента запроса для добавления или обновления кэшированного результата запроса по ключу:
1 |
|
Мутации¶
В отличие от запросов, мутации, обычно, используются для создания/обновления/удаления данных или для выполнения побочных эффектов на сервере. Для этого используется хук useMutation
.
Пример мутации, добавляющей новую задачу на сервер:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
Мутация может находиться в одном из следующих состояний:
isIdle
илиstatus === 'idle'
- мутация находится в режиме ожидания или на стадии обновления/сбросаisLoading
илиstatus === 'loading'
- мутация выполняетсяisError
илиstatus === 'error'
- при выполнении мутации возникла ошибкаisSuccess
илиstatus === 'success'
- мутация выполнена успешно, данные доступны
Также, в зависимости от состояния мутации, доступна дополнительная информация:
error
- если мутация находится в состоянииisError
, ошибка доступна через свойствоerror
data
- если мутация находится в состоянииsuccess
, данные доступны через свойствоdata
Функция mutate
принимает переменную или объект.
При совместном использовании с настройкой onSuccess
, методами invalidateQueries
и setQueryData
мутации становятся очень мощным инструментом.
Обратите внимание: функция mutate
является асинхронной. Это означает, что мы не можем использовать ее в качестве колбека в обработчике событий. Для того, чтобы получить доступ к событию в onSubmit
следует обернуть mutate
в другую функцию.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Сброс состояния мутации¶
В некоторых случаях требуется очистить error
или data
мутации. Для этого используется функция reset
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
Побочные эффекты мутации¶
useMutation
содержит некоторые утилиты, позволяющие выполнять побочные эффекты на любой стадии жизненного цикла мутации. Это может быть полезным как для инвалидации и повторного выполнения запроса после мутации, так и для оптимистического обновления:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
При возвращении промиса из любого колбека, следующий промис будет ждать разрешения предыдущего:
1 2 3 4 5 6 7 8 |
|
При вызове mutate
можно определять дополнительные колбеки, кроме тех, что определены в useMutation
. Это может использоваться для запуска побочных эффектов, специфичных для компонента. Поддерживается перезапись таких методов, как onSuccess
, onError
и onSettled
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Промисы¶
Для получения промиса, который разрешается в случае успеха или выбрасывает ошибку, следует использовать mutateAsync
вместо mutate
. Это может использоваться для создания композиции из побочных эффектов:
1 2 3 4 5 6 7 8 9 10 |
|
Повторное выполнение мутации¶
По умолчанию React Query
не запускает повторное выполнение мутации при возникновении ошибки, но это можно изменить с помощью настройки retry
:
1 2 3 |
|
Сохранение мутации¶
Мутации могут быть сохранены в хранилище с помощью дегидрации/гидрации:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
|
Инвалидация запросов¶
Метод invalidateQueries
клиента запроса позволяет помечать запроса как устаревшие и потенциально выполнять повторное получение данных:
1 2 3 4 |
|
При инвалидации запроса с помощью invalidateQueries
происходит две вещи:
- Он помечается как устаревший. При этом, настройка
staleTime
вuseQuery
и других хуках перезаписывается - Если запрос отрендерен с помощью
useQuery
или других хуков, он выполняется повторно в фоновом режиме
Поиск совпадений с помощью invalidateQueries
¶
При использовании таких API, как invalidateQueries
и removeQueries
(и других, поддерживающих поиск частичного совпадения), можно осуществлять поиск совпадения по префиксу или искать полного совпадения.
В следующем примере мы используем префикс todos
для инвалидации любых запросов, ключи которых начинаются с todos
:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
В метод invalidateQueries
можно передавать переменные для инвалидации конкретных запросов:
1 2 3 4 5 6 7 8 9 10 |
|
API invalidateQueries
является очень гибким, поэтому, если мы хотим аннулировать запросы с ключом todos
без дополнительных переменных или ключей, то можем передать настройку exact: true
:
1 2 3 4 5 6 7 8 9 10 |
|
В invalidateQueries
также можно передавать функцию-предикат. Данная функция будет принимать каждый экземпляр Query
и возвращать true
или false
в зависимости от условия:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Инвалидация запросов с помощью мутаций¶
Инвалидация запросов - это половина успеха. Вторая половина - знать, когда их следует аннулировать. Обычно, при выполнении мутации, связанные с ней запросы нуждаются в инвалидации и повторном выполнении.
Предположим, что у нас имеется мутация для добавления новой задачи:
1 |
|
После выполнении мутации postTodo
, нам требуется аннулировать все запросы с ключом todos
и выполнить их повторно для отображения новой задачи. Для этого мы можем использовать настройку onSuccess
и функцию invalidateQueries
:
1 2 3 4 5 6 7 8 9 10 11 |
|
Инвалидация может выполняться в любом колбеке хука useMutation
.
Выполнение обновлений с помощью данных, возвращаемых мутацией¶
При выполнении мутаций, обновляющих объект на сервере, в ответ, как правило, возвращается обновленный объект. Вместо повторного выполнения запросов, возвращенный объект можно использовать для обновления существующего запроса с помощью метода setQueryData
клиента запроса:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Логику onSuccess
можно вынести в переиспользуемую мутацию (пользовательский хук):
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Оптимистические обновления¶
Обработчик onMutate
позволяет вернуть значение, которое затем передается в обработчики onError
и onSettled
в качестве последнего аргумента. Это может быть использовано для передачи функции отмены.
Обновление списка задач при добавлении новой задачи¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
Обновление одной задачи¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
Вместо обработчиков onError
и onSuccess
можно использовать функцию onSettled
:
1 2 3 4 5 6 7 8 |
|
Отмена запроса¶
React Query
предоставляет общий способ отмены запросов с помощью токена отмены или другого API. Для этого необходимо добавить функцию cancel
к промису, возвращаемому запросом, в котором реализуется логика отмены. Когда запрос становится неактивным или устаревшим, автоматически вызывается функция promise.cancel
.
С помощью axios
¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
С помощью fetch
¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Ручная отмена¶
В случае, когда, например, выполнение запроса занимает слишком много времени, мы можем предоставить пользователю возможность отмены такого запроса. Это можно реализовать с помощью вызова queryClient.cancelQueries(key)
. Если promise.cancel
является доступным, React Query
отменит запрос:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
Фильтры запроса¶
Некоторые методы React Query
принимают объект QueryFilters
. Фильтры запроса - это объект с условиями для поиска совпадения с запросом:
1 2 3 4 5 6 7 8 9 10 11 |
|
Объект QueryFilters
имеет следующие свойства:
exact
- поиск точного совпадения с ключом запросаactive
- поиск совпадения с активными/неактивными запросамиinactive
- поиск совпадения с неактивными/активными запросамиstale
- поиск совпадения с устаревшими/свежими запросамиfetching
- поиск совпадения с запросами, находящимися на стадии выполнения, или со всеми остальными запросамиpredicate
- функция-предикат, вызываемая для каждого запроса и возвращающаяtrue
для найденных запросовqueryKey
- ключ для поиска совпадения
Рендеринг на стороне сервера¶
React Query
поддерживает два способа предварительного получения данных на сервере и передачи их клиенту запроса:
- Самостоятельное предварительное получение данных и их передача в качестве
initialData
- Подходит для быстрой настройки в простых случаях
- Имеет некоторые ограничения
- Предварительное выполнение запроса на сервере, дегидрация кэша и регидрация на клиенте
- Требует дополнительной настройки
Использование Next.js
¶
Рекомендуется использовать Next.js
, который поддерживает две формы предварительного рендеринга:
- Генерация статического контента, статических сайтов (SSG)
- Рендеринг на стороне сервера (SSR)
Использование initialData
¶
С помощью getStaticProps
или getServerSideProps
из Next.js
можно передавать запрашиваемые данные в настройку initialData
хука useQuery
:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Это минимальная настройка, подходящая для простых случаев, но она имеет некоторые ограничения:
- Вызов
useQuery
в глубоко вложенном компоненте предполагает передачуinitialData
в этот компонент - Вызов
useQuery
в нескольких местах предполагает передачуinitialData
во все эти места - Не существует способа определить, когда данные были получены на сервере, поэтому
dataUpdatedAt
и определение того, нуждается ли запрос в повторном выполнении, зависит от времени загрузки страницы
Использование гидрации¶
React Query
поддерживает предварительное выполнение нескольких запросов на сервере в Next.js
и дегидрацию этих запросов на клиенте. Это означает, что сервер может предварительно отрендерить необходимую разметку, которая доступна при загрузке страницы и, как только станет доступным JS, React Query
обновит эти запросы, используя весь функционал библиотеки. Это включает в себя повторное выполнение устаревших запросов.
Для кэширования запросов на сервере и настройки гидрации необходимо сделать следующее:
- Создать новый экземпляр
QueryClient
внутри приложения и в экземпляреref
. Это позволяет предотвратить распределение данных между разными пользователями и запросами - Обернуть компонент приложения в
QueryClientProvider
и передать ему экземпляр клиента - Обернуть компонент приложения в
Hydrate
и передать ему пропdehydrateState
изpageProps
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
После этого необходимо выполнить предварительные запросы на получение данных с помощью getStaticProps
(для SSG) или getServerSideProps
(для SSR):
- Создаем новый экземпляр
QueryClient
для каждой запрашиваемой страницы. Это позволяет предотвратить распределение данных между разными пользователями и запросами - Выполняем предварительный запрос с помощью клиентского метода
prefetchQuery
и ждем его завершения - Используем
dehydrate
для дегидрации кэша запроса и передаем его странице через пропdehydrateState
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
Дефолтная функция запроса¶
При создании клиента запроса, ему можно передать функцию для выполнения запроса, которая будет использоваться по умолчанию. Для этого используется настройка defaultOptions.queries.queryFn
экземпляра queryClient
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
|
Для перезаписи дефолтной функции достаточно передать в запрос другую функцию.
Suspense¶
React Query
может использоваться совместно с React Suspense
. Для включения соответствующего режима следует установить значение настройки suspense
в true
.
Глобальная настройка:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Настройка запроса:
1 2 3 |
|
При включении данного режима состояния status
и объект error
становятся недоступными и заменяются соответствующими элементами React.Suspense
(включая проп fallback
и предохранители для перехвата ошибок).
Мутации также ведут себя несколько иначе. По умолчанию, вместо генерации переменной error
, мутация выбрасывает исключение при следующем рендеринге использующего ее компонента для передачи ближайшему предохранителю. Для отключения такого поведения следует установить настройку useErrorBoundary
в значение false
. Для отключения всех ошибок следует установить настройку throwOnError
также в значение false
.
Сброс ошибок¶
Ошибки запросов могут быть сброшены с помощью компонента QueryErrorResetBoundary
или хука useQueryErrorResetBoundary
.
При использовании компонента будут сброшены все ошибки, перехваченные предохранителем:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
При использовании хука будут сброшены ошибки из ближайшего QueryErrorResetBoundary
. Если предохранитель не определен, ошибки будут сброшены глобально:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
Плагины¶
Обратите внимание: данная технология является экспериментальной и может измениться в будущем.
persistQueryClient¶
persistQueryClient
- это утилита, позволяющая сохранять клиента запроса и его кэш для последующего использования. На сегодняшний день для этого доступен только один плагин - createLocalStoragePersistor
.
Импорт:
1 |
|
Пример использования.
Импортируем функцию persistQueryClient
и передаем ей экземпляр QueryClient
(с настройкой cacheTime
) и интерфейс Persistor
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
API¶
persistQueryClient
- принимает экземплярQueryClient
иpersistor
1 |
|
Настройки:
queryClient
- клиент запросаpersistor
- интерфейс для сохранения/восстановления кэшаmaxAge:
- максимальный возраст кэшаbuster
- уникальная строка, которая может использоваться для принудительной инвалидации кэша
Дефолтные настройки:
1 2 3 4 |
|
createLocalStoragePersistor¶
Импорт:
1 |
|
Пример использования.
- Импортируем функцию
createLocalStoragePersistor
- Создаем новый
localStoragePersistor
- Передаем его в функцию
persistQueryClient
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
API¶
createLocalStoragePersistor
- принимает опциональный объект с настройками и возвращаетlocalStoragePersistor
, передаваемый вpersisteQueryClient
1 |
|
Настройки:
localStorageKey
- ключ, используемый для сохранения кэшаthrottleTime
- "троттлинг" записи значений в хранилище
Дефолтные настройки:
1 2 3 4 |
|
broadcastQueryClient¶
broadcastQueryClient
- это утилита для распространения (распределения, вещания) и синхронизации состояния клиента запроса между вкладками/окнами браузера одного источника (same origin).
Импорт:
1 |
|
Пример использования.
Импортируем функцию broadcastQueryClient
и передаем ей экземпляр QueryClient
и, опционально, устанавливаем broadcastChannel
:
1 2 3 4 5 6 7 8 |
|
API¶
broadcastQueryClient
- принимает экземплярQueryClient
и, опционально,broadcastChannel
1 |
|
Настройки:
queryClient
- клиент запросаbroadcastChannel
- уникальное название канала, которое будет использоваться для коммуникации между вкладками/окнами
Дефолтные настройки:
1 2 3 |
|
Основные части API¶
useQuery¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
|
Основные настройки¶
queryKey | array
- уникальная строка или массив: ключ для идентификации запроса. Запрос автоматически обновляется при изменении данного ключа (еслиenabled
не установлено вfalse
)queryFn
- функция для получения данных. Принимает объектQueryFunctionContext
с переменнойqueryKey
. Должна возвращать промис, разрешающийся данными или выбрасывающий исключениеenabled
- автоматическое выполнение запросаretry
- количесто попыток выполнения запроса (true
- бесконечное количество попыток)retryDelay
- функция, принимающее целое числоretryAttempt
и ошибку и возвращающая задержку, применяемую перед следующей попыткой выполнения запроса в мс. Функцияattempt => Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000)
применяет экспоненциальную задержку. Функцияattempt => attempt * 1000
применяет линейную задержкуstaleTime
- время в мс, по истечении которого данные считаются устаревшими (Infinity
- данные всегда будут считаться свежими)cacheTime
- время в мс, в течение которого неиспользуемый/неактивный кэш сохраняется в памяти, после чего такой кэш уничтожается сборщиком мусора (Infinity
- кэш никогда не будет собран)refetchInterval
- периодическое повторное выполнение запроса (период определяется в мс)notifyOnChangeProps
- компонент будет перерисовываться только при изменении указанных свойств. Например, при указании['data', 'error']
, компонент будет перерисовываться только при измененииdata
илиerror
.tracked
означает отслеживание доступа к свойствам: компонент будет перерисовываться только при изменении одного из отслеживаемых свойствonSuccess
- функция, вызываемая при успехе запроса. Получает объектdata
onError
- функция, вызываемая при провале запроса. Получает объектerror
onSettled
- функция, вызываемая как при успехе, так и при провале запроса. Получает объектыdata
иerror
select
- функция, используемая для преобразования или выборки данныхinitialData
- начальное значение для кэша запроса. Если значением является функция, она будет вызвана только один раз при инициализации запроса и должна синхронно возвращать начальные данные. Начальные данные считаются устаревшими, если не установленоstaleTime
initialDataUpdatedAt
- время в мс последнего обновленияinitialData
placeholderData
- данные-заменители, используемые до получения настоящих данных и при отсутствииinitialData
keepPreviousData
- сохранение предыдущих данных при запросе новых (при изменении ключа запроса)
Основные возвращаемые значения¶
status
- возможные значения:idle
- возможно только при инициализации запроса сenabled: false
и отсутствии начальных данныхloading
- запрос находится на стадии выполненияerror
- выполнение запроса завершилось ошибкойsuccess
- выполнение запроса завершилось успешно. Соответствующее свойствоdata
- это данные, полученные из успешного запроса, или, приenabled: false
, начальные данные- производные логические значения из переменной
status
: isIdle
isLoading
isError
isSuccess
data
- последние успешно разрешенные данные для запроса (по умолчаниюundefined
)error
- объект ошибки для запроса (по умолчаниюnull
)isFetching
-true
, если запрос находится в процессе выполнения (включая выполнение в фоновом режиме)refetch
- функция для ручного выполнения повторного запросаremove
- функция для удаления запроса из кэша
useQueries¶
Хук useQueries
может использоваться для выполнения нескольких запросов:
1 2 3 4 |
|
Хук принимает массив объектов с настройками запроса и возвращает массив результатов выполнения этих запросов.
useInfiniteQuery¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Настройки¶
Настройки для useInfiniteQuery
идентичны настройкам useQuery
, кроме следующих:
queryFn
- функция для получения данных. Принимает объектQueryFunctionContext
с переменнымиqueryKey
иpageParam
. Должна возвращать промис, разрешающийся данными или выбрасывающий исключение. Убедитесь, что возвращаете данные иpageParam
, если собираетесь использовать последнийgetNextPageParam
- при получении новых данных запросом, эта функция получает последнюю страницу бесконечного списка данных и массив всех страниц. Функция должна возвращать переменную, которая передается в качестве последнего опционального аргумента в функцию запроса. Возвращаетundefined
при отсутствии следующей страницыgetPreviousPageParam
- аналогична функцииgetNextPageParam
, но в отношении предыдущей страницы
Основные возвращаемые значения¶
Значения, возвращаемые useInfiniteQuery
, идентичны значениям, возвращаемым useQuery
, кроме следующих:
data.pages
- массив, содержащий все страницыdata.pageParams
- массив, содержащий все параметры страницыfetchNextPage
- функция, позволяющая получать следующую "страницу" результатов.options.pageParam
позволяет вручную определять параметр страницы вместо использованияgetNextPageParam
fetchPreviousPage
- аналогична функцииfetchNextPage
, но в отношении предыдущей страницыhasNextPage
- имеется ли следующая страница для полученияhasPreviousPage
- имеется ли предыдущая страница для получения
useMutation¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
Настройки¶
mutationFn
- функция, выполняющая асинхронную задачу и возвращающая промис. Принимает объектvariables
для мутированияmutationKey
- строка, которая может использовать для наследования дефолтных настроек с помощьюqueryClient.setMutationDefaults
, а также для идентификации мутации в инструментах разработчикаonMutate
- функция, выполняемая перед мутацией, принимающая те же переменные. Может использоваться для выполнения оптимистических обновлений в надежде, что мутация завершится успешно. В случае провала мутации, возвращаемое этой функцией значение будет передано вonError
иonSettled
onSuccess
- функция, выполняемая при успехе мутации. Если возвращается промис, он будет разрешен перед продолжениемonError
- функция, выполняемая при провале мутации. Если возвращается промис, он будет разрешен перед продолжениемonSettled
- функция, выполняемая как при успехе, так и при провале мутации. Если возвращается промис, он будет разрешен перед продолжениемretry
- количество попыток повторного выполнения мутации (true
- бесконечное количество попыток)retryDelay
- аналогичнаretryDelay
запросаuseErrorBoundary
- еслиtrue
, ошибки, возникшие при выполнении мутации, выбрасываются на стадии рендеринга и передаются ближайшему предохранителю
Возвращаемые значения¶
mutate
- функция мутации, которая может вызываться с переменными для запуска мутации и, опционально, для перезаписи настроек, переданных вuseMutation
mutateAsync
- функция, аналогичнаяmutate
, но возвращающая промисstatus
- возможные значения:idle
loading
error
success
isIdle
,isLoading
,isError
,isSuccess
- производные логические значенияstatus
data
- последние успешно разрешенные данные для запроса (по умолчаниюundefined
)error
- объект ошибки для запроса (по умолчаниюnull
)reset
- функция для очистки внутреннего состояния мутации
useIsFetching¶
useIsFetching
- опциональный хук, возвращающий количество запросов, выполняемых в фоновом режиме. Может использоваться для глобальных индикаторов загрузки:
1 2 3 4 5 |
|
Настройки:
queryKey
- ключ запросаfilters
- фильтры запроса
Возвращается количество запросов, выполняемых в фоновом режиме.
QueryClient¶
QueryClient
может использоваться для взаимодействия с кэшем:
1 2 3 4 5 6 7 8 9 10 11 |
|
Доступные методы:
fetchQuery
fetchInfiniteQuery
prefetchQuery
prefetchInfiniteQuery
getQueryData
setQueryData
getQueryState
invalidateQueries
refetchQueries
cancelQueries
removeQueries
resetQueries
isFetching
getDefaultOptions
setDefaultOptions
getQueryDefaults
setQueryDefaults
getMutationDefaults
setMutationDefaults
getQueryCache
getMutationCache
clear
Настройки¶
queryCache
- кэш запроса, к которому подключен данный клиентmutationCache
- кэш мутации, к которому подключен данный клиентdefaultOptions
- настройки по умочанию для всех запросов и мутаций
Методы¶
fetchQuery
¶
fetchQuery
- это асинхронный метод, который может использоваться для выполнения и кэширования запроса. Если нам не нужен результат запроса, то вместо fetchQuery
можно использовать prefetchQuery
.
Если запрос существует и данные не аннулированы или не старше указанного staleTime
, то возвращаются данные из кэша. В противном случае, предпринимается попытка получения последних данных.
Разница между fetchQuery
и setQueryData
состоит в том, что fetchQuery
является асинхронным и позволяет убедиться, что для данного запроса с помощью экземпляров useQuery
не было создано дублирующихся запросов (для отрендеренного запроса во время получения данных).
1 2 3 4 5 6 7 8 |
|
С помощью staleTime
можно обеспечить выполнение запроса, когда данные становятся устаревшими:
1 2 3 4 5 6 7 8 9 10 11 |
|
fetchInfiniteQuery
¶
fetchInfiniteQuery
аналогичен fetchQuery
, но используется для выполнения и кэширования бесконечных запросов:
1 2 3 4 5 6 7 8 9 |
|
prefetchQuery
¶
prefetchQuery
- это асинхронный метод, который может использоваться для предварительного выполнения запроса, перед его выполнением или рендерингом с помощью useQuery
. В целом, данный метод работает так же, как fetchQuery
.
prefetchInfiniteQuery
¶
prefetchInfiniteQuery
аналогичен prefetchQuery
, но используется для предварительного выполнения и кэширования бесконечных запросов.
getQueryData
¶
getQueryData
- синхронная функция для получения существующих кэшированных данных запроса. Если запроса не существует, возвращается undefined
.
1 |
|
setQueryData
¶
setQueryData
- синхронная функция для незамедлительного обновления кэшированных данных запроса. Если запроса не существует, он создается. Если запрос не используется хуком в течение дефолтных пяти минут cacheTime
, он уничтожается сборщиком мусора.
1 |
|
getQueryState
¶
getQueryState
- синхронная функция для получения существующего состояния запроса. Если запроса не существует, возвращается undefined
1 2 |
|
invalidateQueries
¶
Метод invalidateQueries
используется для инвалидации и обновления одного или нескольких запросов в кэше на основе их ключей или других доступных свойств/состояния. По умолчанию, все совпавшие запросы помечаются как аннулированные и активные запросы повторно выполняются в фоновом режиме.
- Для отключения повторного выполнения активных запросов используется настройка
refetchActive: false
- Для повторного выполнения неактивных запросов используется настройка
refetchInactive: true
1 2 3 4 5 6 7 8 9 |
|
refetchQueries
¶
Метод refetchQueries
используется для повторного выполнения запросов на основе определенных условий.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
cancelQueries
¶
Метод cancelQueries
используется для отмены исходящих запросов на основе их ключей или любых других доступных свойств/состояния.
Это может быть полезным при выполнении оптимистических обновлений, когда мы не хотим, чтобы выполняющиеся запросы перезаписывали такие обновления.
1 |
|
removeQueries
¶
Метод removeQueries
используется для удаления запросов из кэша на основе их ключей или любых других доступных свойств/состояния.
1 |
|
resetQueries
¶
Метод resetQueries
используется для сброса запросов в кэше к их начальному состоянию на основе ключей или любых других доступных свойств/состояния.
Данный метод уведомляет подписчиков, в отличие от clear
, который удаляет подписчиков. Если запрос имеет initialData
, он будет сброшен к ним. Если запрос является активным, он будет выполнен повторно.
1 |
|
isFetching
¶
Метод isFetching
похож на хук useIsFetching
. Он возвращает количество запросов, находящихся в процессе выполнения.
1 2 3 4 5 |
|
getDefaultOptions
и setDefaultOptions
¶
getDefaultOptions
возвращает настройки по умолчанию, определенные при создании клиента или с помощью setDefaultOptions
. setDefaultOptions
позволяет динамически устанавливать дефолтные настройки для данного клиента.
getQueryDefaults
и setQueryDefaults
¶
Аналогичны getDefaultOptions
и setDefaultOptions
, но в отношении конкретных запросов.
getMutationDefaults
и setMutationDefaults
¶
Аналогичны getDefaultOptions
и setDefaultOptions
, но в отношении конкретных мутаций.
getQueryCache
и getMutationCache
¶
Данные методы возвращают, соответственно, кэш запроса и кэш мутации, к которым подключен данный клиент.
clear
¶
Метод clear
очищает все подключенные кэши.
1 |
|
QueryClientProvider¶
QueryClientProvider
используется для подключения и передачи QueryClient
в приложение:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
useQueryClient¶
Хук useQueryClient
возвращает текущий экземпляр QueryClient
:
1 2 3 |
|
QueryObserver¶
QueryObserver
используется для наблюдения и переключения между запросами:
1 2 3 4 5 6 7 8 |
|
InfiniteQueryObserver¶
InfiniteQueryObserver
используется для наблюдения и переключения между бесконечными запросами:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
QueriesObserver¶
QueriesObserver
используется для наблюдения за несколькими запросами:
1 2 3 4 5 6 7 8 9 |
|