0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p><strong>RTK Query</strong>- это инструмент для создания сервисов для запросов на сервер. Он позволяет создавать удобные интерфейсы, благодаря которым мы можем отслеживать состояния запросов и при этом не создавать много однообразного кода.</p>
1
<p><strong>RTK Query</strong>- это инструмент для создания сервисов для запросов на сервер. Он позволяет создавать удобные интерфейсы, благодаря которым мы можем отслеживать состояния запросов и при этом не создавать много однообразного кода.</p>
2
<h2>Для чего нужен RTK Query</h2>
2
<h2>Для чего нужен RTK Query</h2>
3
<p>Часто в приложениях мы делаем запросы к серверу. Чтобы отслеживать состояние этих запросов, приходится писать много однотипного кода. Даже createAsyncThunk() не сильно помогает с такой задачей:</p>
3
<p>Часто в приложениях мы делаем запросы к серверу. Чтобы отслеживать состояние этих запросов, приходится писать много однотипного кода. Даже createAsyncThunk() не сильно помогает с такой задачей:</p>
4
<p>На каждое изменение запроса приходится добавлять свой редюсер, при этом код не сильно отличается. А если мы захотим отслеживать состояние каждого запроса по отдельности, то придется для этого еще и расширять состояние, чтобы хранить в нем все статусы запросов и ошибки.</p>
4
<p>На каждое изменение запроса приходится добавлять свой редюсер, при этом код не сильно отличается. А если мы захотим отслеживать состояние каждого запроса по отдельности, то придется для этого еще и расширять состояние, чтобы хранить в нем все статусы запросов и ошибки.</p>
5
<p>Для решения таких задач был создан инструмент RTK Query. Он позволяет создать API для запросов на сервер с минимумом кода. Под капотом он использует createSlice и createAsyncThunk, но делает их использование более удобным.</p>
5
<p>Для решения таких задач был создан инструмент RTK Query. Он позволяет создать API для запросов на сервер с минимумом кода. Под капотом он использует createSlice и createAsyncThunk, но делает их использование более удобным.</p>
6
<p>RTK Query имеет множество различных настроек и функций. В этом уроке мы не будем подробно разбирать весь функционал, а коснемся только основного.</p>
6
<p>RTK Query имеет множество различных настроек и функций. В этом уроке мы не будем подробно разбирать весь функционал, а коснемся только основного.</p>
7
<h2>Создание API</h2>
7
<h2>Создание API</h2>
8
<p>Рассмотрим создание простого API для четырех роутов:</p>
8
<p>Рассмотрим создание простого API для четырех роутов:</p>
9
<p>Здесь мы создали API с помощью функции createApi(). Функция принимает объект, описывающий наше API:</p>
9
<p>Здесь мы создали API с помощью функции createApi(). Функция принимает объект, описывающий наше API:</p>
10
<ul><li>reducerPath - название для стейта, можно применить при создании слайса</li>
10
<ul><li>reducerPath - название для стейта, можно применить при создании слайса</li>
11
<li>baseQuery - базовый URL для запросов. Здесь мы использовали функцию fetchBaseQuery() для создания URL, этот URL будут использовать все запросы в API</li>
11
<li>baseQuery - базовый URL для запросов. Здесь мы использовали функцию fetchBaseQuery() для создания URL, этот URL будут использовать все запросы в API</li>
12
<li>endpoints - здесь мы описываем сами запросы. В свойство передается функция, которая принимает объект builder - он позволяет настраивать API. Функция возвращает объект, в котором каждое свойство описывает нужный запрос.</li>
12
<li>endpoints - здесь мы описываем сами запросы. В свойство передается функция, которая принимает объект builder - он позволяет настраивать API. Функция возвращает объект, в котором каждое свойство описывает нужный запрос.</li>
13
</ul><p>Разберем подробнее объект, который возвращается из функции в endpoints. Как следует из названия, функция создает<strong>эндпоинты</strong>(<em>конечные точки</em>).</p>
13
</ul><p>Разберем подробнее объект, который возвращается из функции в endpoints. Как следует из названия, функция создает<strong>эндпоинты</strong>(<em>конечные точки</em>).</p>
14
<p>Эндпоинт - это маршрут, по которому мы должны обращаться к серверу. В примере выше сервер предоставляет четыре эндпоинта для каждого действия: получение списка пользователей, получение одного пользователя, добавление и удаление пользователя.</p>
14
<p>Эндпоинт - это маршрут, по которому мы должны обращаться к серверу. В примере выше сервер предоставляет четыре эндпоинта для каждого действия: получение списка пользователей, получение одного пользователя, добавление и удаление пользователя.</p>
15
<p>Каждый эндпоинт настраивается с помощью объекта builder. Для простых эндпоинтов, которые должны сделать get-запрос, используется метод builder.query(). В метод передается объект со свойством query() - это функция, формирующая запрос.</p>
15
<p>Каждый эндпоинт настраивается с помощью объекта builder. Для простых эндпоинтов, которые должны сделать get-запрос, используется метод builder.query(). В метод передается объект со свойством query() - это функция, формирующая запрос.</p>
16
<p>Для формирования первого эндпоинта, получающего список пользователя, мы определили функцию query: () => ''. Здесь мы возвращаем строку, потому что базовый URL уже содержит адрес получения списка пользователей: /api/users.</p>
16
<p>Для формирования первого эндпоинта, получающего список пользователя, мы определили функцию query: () => ''. Здесь мы возвращаем строку, потому что базовый URL уже содержит адрес получения списка пользователей: /api/users.</p>
17
<p>Следующий эндпоинт уже изменяет этот адрес, потому что нам нужно добавить идентификатор пользователя: query: (id) => id. Здесь id добавится к базовому адресу /api/users/:id.</p>
17
<p>Следующий эндпоинт уже изменяет этот адрес, потому что нам нужно добавить идентификатор пользователя: query: (id) => id. Здесь id добавится к базовому адресу /api/users/:id.</p>
18
<p>Для эндпоинтов, которые вносят изменения в данные, мы можем использовать метод builder.mutation(). Он работает похожим образом как query(), но позволяет больше настроить запрос - изменять HTTP-метод или добавлять тело запроса.</p>
18
<p>Для эндпоинтов, которые вносят изменения в данные, мы можем использовать метод builder.mutation(). Он работает похожим образом как query(), но позволяет больше настроить запрос - изменять HTTP-метод или добавлять тело запроса.</p>
19
<h2>Подключение API к стору</h2>
19
<h2>Подключение API к стору</h2>
20
<p>Разберемся, как подключить наше API к стору:</p>
20
<p>Разберемся, как подключить наше API к стору:</p>
21
<p>Созданное API предоставляет редюсеры, а также мидлвару. Как видите, основной подход не меняется, к стору точно так же подключаются новые редюсеры и мидлвара. Здесь мы используем reducerPath для указания имени группы редюсеров. Нам не нужно создавать слайс и описывать редюсеры - благодаря createApi(), все это сделано за нас.</p>
21
<p>Созданное API предоставляет редюсеры, а также мидлвару. Как видите, основной подход не меняется, к стору точно так же подключаются новые редюсеры и мидлвара. Здесь мы используем reducerPath для указания имени группы редюсеров. Нам не нужно создавать слайс и описывать редюсеры - благодаря createApi(), все это сделано за нас.</p>
22
<h2>Кэш</h2>
22
<h2>Кэш</h2>
23
<p>В основе состояния RTK Query лежит кэш. При первом запросе RTK Query отправляет запрос и сохраняет полученные данные в кэше. При последующих запросах к тому же эндпоинту RTK Query проверяет кэш на наличие сохраненных данных и, если они есть, возвращает их, не делая запроса. Это позволяет снизить количество запросов к API и улучшить производительность приложения.</p>
23
<p>В основе состояния RTK Query лежит кэш. При первом запросе RTK Query отправляет запрос и сохраняет полученные данные в кэше. При последующих запросах к тому же эндпоинту RTK Query проверяет кэш на наличие сохраненных данных и, если они есть, возвращает их, не делая запроса. Это позволяет снизить количество запросов к API и улучшить производительность приложения.</p>
24
<p>По умолчанию кэш устаревает за 60 секунд. После этого RTK Query пометит кэш как устаревший, и при новом запросе обновит его. Можно вручную задать время жизни кэша с помощью свойства keepUnusedDataFor:</p>
24
<p>По умолчанию кэш устаревает за 60 секунд. После этого RTK Query пометит кэш как устаревший, и при новом запросе обновит его. Можно вручную задать время жизни кэша с помощью свойства keepUnusedDataFor:</p>
25
<h2>Хуки для компонентов</h2>
25
<h2>Хуки для компонентов</h2>
26
<p>Теперь осталось разобрать, как использовать API в компонентах. Созданное API предоставляет автоматически сгенерированные хуки для каждого действия:</p>
26
<p>Теперь осталось разобрать, как использовать API в компонентах. Созданное API предоставляет автоматически сгенерированные хуки для каждого действия:</p>
27
<p>Это позволяет сразу использовать API в компонентах без диспатча, что очень удобно. Ниже хук, созданный builder.query():</p>
27
<p>Это позволяет сразу использовать API в компонентах без диспатча, что очень удобно. Ниже хук, созданный builder.query():</p>
28
<p>Пример хука для запроса с параметром:</p>
28
<p>Пример хука для запроса с параметром:</p>
29
<p>Вызывая хук в компоненте, мы таким образом подписываем этот компонент на все изменения состояния. При обновлении кэша этого энпдоинта, произойдет перерисовка всех компонентов, в которых вызван хук этого эндпоинта.</p>
29
<p>Вызывая хук в компоненте, мы таким образом подписываем этот компонент на все изменения состояния. При обновлении кэша этого энпдоинта, произойдет перерисовка всех компонентов, в которых вызван хук этого эндпоинта.</p>
30
<p>Хук дает все необходимое, чтобы отслеживать запрос:</p>
30
<p>Хук дает все необходимое, чтобы отслеживать запрос:</p>
31
<ul><li>isLoading - если нам нужно отследить загрузку, например, для блокирования формы на время отправки запроса</li>
31
<ul><li>isLoading - если нам нужно отследить загрузку, например, для блокирования формы на время отправки запроса</li>
32
<li>status - содержит строковое значение текущего состояния запроса. Дает больше информации, чем isLoading</li>
32
<li>status - содержит строковое значение текущего состояния запроса. Дает больше информации, чем isLoading</li>
33
<li>isError - если во время запроса была ошибка, то будет равен true</li>
33
<li>isError - если во время запроса была ошибка, то будет равен true</li>
34
<li>error - содержит данные ошибки, если возникла</li>
34
<li>error - содержит данные ошибки, если возникла</li>
35
<li>data - результат запроса</li>
35
<li>data - результат запроса</li>
36
<li>refetch() - при вызове этой функции, RTK Query пометит текущее состояние эндпоинта как устаревшее и вызовет новый запрос для обновления кэша. Все компоненты, которые подписаны на это состояние, будут перерисованы</li>
36
<li>refetch() - при вызове этой функции, RTK Query пометит текущее состояние эндпоинта как устаревшее и вызовет новый запрос для обновления кэша. Все компоненты, которые подписаны на это состояние, будут перерисованы</li>
37
</ul><p>Это только некоторые свойства, но уже их будет достаточно для большинства задач.</p>
37
</ul><p>Это только некоторые свойства, но уже их будет достаточно для большинства задач.</p>
38
<p>Для builder.mutation() хук выглядит несколько сложней:</p>
38
<p>Для builder.mutation() хук выглядит несколько сложней:</p>
39
<p>Такие хуки уже возвращают массив. Первый элемент массива - это функция, с помощью которой мы можем вызывать запрос. А второй элемент - уже знакомый нам объект, через который мы можем отслеживать состояние запроса. Как видите, каждый хук предоставляет свое состояние для каждого запроса. Это позволяет не писать однотипный код и вручную заполнять состояние и отслеживать процессы.</p>
39
<p>Такие хуки уже возвращают массив. Первый элемент массива - это функция, с помощью которой мы можем вызывать запрос. А второй элемент - уже знакомый нам объект, через который мы можем отслеживать состояние запроса. Как видите, каждый хук предоставляет свое состояние для каждого запроса. Это позволяет не писать однотипный код и вручную заполнять состояние и отслеживать процессы.</p>
40
<p>Благодаря тому, что RTK Query использует кэш, при использовании множества одинаковых хуков, происходит только один запрос на сервер. Кэш сохраняется вместе с параметрами. Например, для запросов /user/1 и /user/2 будут созданы разные кэши</p>
40
<p>Благодаря тому, что RTK Query использует кэш, при использовании множества одинаковых хуков, происходит только один запрос на сервер. Кэш сохраняется вместе с параметрами. Например, для запросов /user/1 и /user/2 будут созданы разные кэши</p>
41
<h2>Функция запроса</h2>
41
<h2>Функция запроса</h2>
42
<p>RTK Query позволяет полностью заменить способ отправки запроса. Для этого используется свойство queryFn. В него передается функция, которая возвращает данные:</p>
42
<p>RTK Query позволяет полностью заменить способ отправки запроса. Для этого используется свойство queryFn. В него передается функция, которая возвращает данные:</p>
43
<p>Это может потребоваться, например, чтобы заменить http-клиент.</p>
43
<p>Это может потребоваться, например, чтобы заменить http-клиент.</p>
44
<h2>Теги</h2>
44
<h2>Теги</h2>
45
<p>Частая задача, когда данные зависят от других данных. Например, мы удаляем пользователя, сервер при этом удаляет все сообщения этого же пользователя. Теперь список сообщений в приложении считается устаревшим, так как может содержать сообщения удаленного пользователя. В RTK Query для такой задачи используется механизм тегов. Каждый эндпоинт мы можем пометить одним или несколькими тегами. В тех эндпоинтах, где происходит изменение состояния, мы отмечаем теги, которые зависят от изменяемого состояния. Разберем пример:</p>
45
<p>Частая задача, когда данные зависят от других данных. Например, мы удаляем пользователя, сервер при этом удаляет все сообщения этого же пользователя. Теперь список сообщений в приложении считается устаревшим, так как может содержать сообщения удаленного пользователя. В RTK Query для такой задачи используется механизм тегов. Каждый эндпоинт мы можем пометить одним или несколькими тегами. В тех эндпоинтах, где происходит изменение состояния, мы отмечаем теги, которые зависят от изменяемого состояния. Разберем пример:</p>
46
<p>В примере выше для эндпоинтов getUsers и getComments задан тег User с помощью свойства providesTags, в него передается список тегов. Для эндпоинта removeUser передан этот же тег User в свойство invalidatesTags. Теперь, когда будет происходить запрос на удаления пользователя, RTK Query инвалидирует указанные теги. Это значит, что все кэши эндпоинтов с заданным тегом будут помечены как устаревшие, и данные будут загружены повторно. В нашем случае это будут эндпоинты getUsers и getComments.</p>
46
<p>В примере выше для эндпоинтов getUsers и getComments задан тег User с помощью свойства providesTags, в него передается список тегов. Для эндпоинта removeUser передан этот же тег User в свойство invalidatesTags. Теперь, когда будет происходить запрос на удаления пользователя, RTK Query инвалидирует указанные теги. Это значит, что все кэши эндпоинтов с заданным тегом будут помечены как устаревшие, и данные будут загружены повторно. В нашем случае это будут эндпоинты getUsers и getComments.</p>
47
<p>Теги могут содержать уникальную информацию:</p>
47
<p>Теги могут содержать уникальную информацию:</p>
48
<p>В примере выше теги формируются с помощью функций. Каждая функция возвращает объект вида { type, id }. Это позволяет манипулировать кэшами отдельных сущностей. Например, при обновлении пользователя с конкретным id, эндпоинт getUser обновит данные этого пользователя, но кэши остальных пользователей останутся без изменений.</p>
48
<p>В примере выше теги формируются с помощью функций. Каждая функция возвращает объект вида { type, id }. Это позволяет манипулировать кэшами отдельных сущностей. Например, при обновлении пользователя с конкретным id, эндпоинт getUser обновит данные этого пользователя, но кэши остальных пользователей останутся без изменений.</p>