**Введение
RTK Query — это мощный инструмент для получения и кэширования данных. Он предназначен для упрощения распространенных случаев загрузки данных в веб-приложении, избавляя от необходимости вручную писать логику загрузки и кэширования данных.
RTK Query
— это дополнительный аддон, включенный в пакет Redux Toolkit
, и его функциональность построена поверх других API в Redux Toolkit.
**Особенности RTK Query
- Под капотом RTK Query использует
createSlice
иcreateAsyncThunk
, которые предоставляет API Redux Toolkit. - В RTQ Query взаимодействие с API задается с помощью endpoint, которые определены в момент инициализации API (метод
createApi
, о котором пойдет речь ниже); - RTK Query автоматически создает хуки, исходя из заданных эндпоинтов. Данные хуки могут быть использованы непосредственно в React компонентах для загрузки/отображения/изменения данных. Механизм взаимодействия с API инкапсулирован.
- RTK Query поддерживает кэширование из коробки.
- Позволяет решить проблему дедубликации запросов, например, если два компонента на одной странице совершают один и тот же запрос к API, выполнен будет лишь один запрос.
**Недостатки RTK Query
- В реальном проекте может быть несколько файлов с различными блоками API, и в этом случае возникает вопрос, как связать всё вместе.
- Проблема решается тем, что все действия по загрузке, мутированию обновлению кэша, инвалидации в RTQ Query могут быть вызваны с использованием dispatch из любого места приложения.
- Система тегов непроста и не так прозрачна, как у альтернатив.
**RTK Query
на практике
Начнем с создания с так называемого “API Slice”
, в котором определим базовый URL сервера и эндпоинты, с которым нужно будет взаимодействовать.
Определим три главных параметра:
reducerPath
. Уникальный ключ, который будет добавлен в storebaseQuery
. Параметр baseQuery отвечает за непосредственное взаимодействие с API. В состав RTK Query входит инструмент под названиемfetchBaseQuery
, представляющий собой легковесную обертку над fetch, подходящий для большинства операций по работе с API.endPoints
. Это набор взаимодействий с API. Существует два вида endpoint: query и mutation
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"
export const starWarsApi = createApi({
reducerPath: "starWars",
baseQuery: fetchBaseQuery({ baseUrl: "https://swapi.dev/api" }),
endpoints: (builder) => ({
// Define endpoints here
}),
})
Как было указано выше, существует два вида endpoint: query
и mutation
. Зададим два query endpoint’а:
endpoints: (builder) => ({
getFilms: builder.query({
query: () => `/films?format=json`,
}),
getFilmById: builder.query({
query: (filmId) => `/films/${filmId}?format=json`,
}),
})
В коде выше были добавлены два endpoint:
- getFilms. Получение списка всех фильмов;
- getFilmById. Получение одного фильма по его ID. В данном случае filmId представляет собой query параметр; при необходимости набор параметров можно расширить.
Самое интересное начинается здесь.
Для каждого endpoint, объявленного выше, RTK Query
автоматически генерирует хуки, которые могут быть использован в React компонентах для загрузки/изменения данных. Рассмотрим это на следующем примере:
Как видно на изображении, starWarsApi после инициализации содержит в себе сгенерированные хуки. В зависимости от типов endpoint, название хуков будет содержать в себе либо query, либо mutation.
Финальная версия Star Wars API Slice выглядит следующим образом:
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"
export const starWarsApi = createApi({
reducerPath: "starWars",
baseQuery: fetchBaseQuery({ baseUrl: "https://swapi.dev/api" }),
endpoints: (builder) => ({
getFilms: builder.query({
query: () => `/films?format=json`,
}),
getFilmById: builder.query({
query: (filmId) => `/films/${filmId}?format=json`,
}),
}),
})
export const { useGetFilmsQuery, useGetFilmByIdQuery } = starWarsApi
Метод createApi генерирует reducer
, который должен быть добавлен в store
. Также для обеспечения возможностей RTK Query
(кэширование, инвалидация, polling и др.) необходимо добавить middleware
как на примере ниже:
import { configureStore } from "@reduxjs/toolkit"
import { setupListeners } from "@reduxjs/toolkit/query"
import { starWarsApi } from "./services/starWarsApi"
export const store = configureStore({
reducer: {
// Add the generated reducer as a specific top-level slice
[starWarsApi.reducerPath]: starWarsApi.reducer,
}, // Adding the api middleware enables caching, invalidation, polling,
// and other useful features of `rtk-query`.
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(starWarsApi.middleware),
})
// optional, but required for refetchOnFocus/refetchOnReconnect behaviors
// see `setupListeners` docs - takes an optional callback as the 2nd arg for customization
setupListeners(store.dispatch)
Настройка на этом завершена. Далее рассмотрим использование сгенерированных хуков в React компонентах.
import { useGetFilmsQuery } from "../reduxStore/services/starWarsApi"
const FilmsList = () => {
const { data, isLoading, error } = useGetFilmsQuery()
return (
<div>
<h3>Star Wars Movies</h3> {" "}
{error ? (
<>Oh no, there was an error</>
) : isLoading ? (
<>Loading...</>
) : data ? (
<div>
{" "}
{data.results.map((movie) => (
<section item key={movie.episode_id} xs={4}>
<h2>{movie.title}</h2> <p>{movie.opening_crawl}</p>
{" "}
</section>
))}
{" "}
</div>
) : null}
{" "}
</div>
)
}
export default FilmsList
Рассмотрим описанный код выше:
- Сначала мы просто импортировали хук useGetFilmsQuery
- При вызове хука useGetFilmsQuery будет автоматически производиться вызов к API для получения всех фильмов. Хук в свою очередь возвращает не только вышеуказанные значения, но и также ряд других полезных, таких как isFetching, isError и другие.
- Теперь не приходится создавать
action creators
для каждого запроса - Нет нужды создавать множество
reducers
- Обработка состояний запросов (isFetching, isError и др.) теперь производится автоматически
- В React компонентах не нужно вызывать метод
dispatch
или использоватьселекторы
для взаимодействия соstore
В результате количество написанного кода становится меньше, а его восприятие заметно улучшилось.