React: Redux Toolkit
2026-02-26 16:49 Diff

Проблематика работы с асинхронными запросами

Одна из самых сложных задач в построении фронтенд-приложений – работа с внешними запросами. Трудности приходят с двух сторон.

С одной стороны, асинхронность сама по себе порождает неоднозначности, перестают работать стандартные механизмы. Redux не умеет работать в асинхронном режиме, поэтому вся обработка запросов происходит снаружи. В таком случае любая нетривиальная логика обработки асинхронных действий будет появляться внутри компонентов React:

С другой стороны, сеть — это вещь ненадежная. Запросы могут выполняться долго или не выполниться вообще, и все это нужно отслеживать для правильной реакции:

  • При долгих запросах — показывать спиннер
  • При обрыве запроса — выводить соответствующее предупреждение

Добавим к примеру выше обработку ошибок и отслеживание статуса загрузки:

Как мы видим, даже для небольшого числа вызовов придется написать очень много похожего кода. В реальных приложениях количество вызовов может измеряться многими десятками и даже сотнями. Поэтому без готового решения тут не обойтись. Для автоматизации HTTP-запросов к нам на помощь приходят два механизма:

  • Мидлвара redux-thunk, которая уже включена в Redux Toolkit
  • Механизм createAsyncThunk()

Мидлвара redux-thunk

Мидлвара — это код, который встраивается в обработку. Можно представить ее в виде цепочки функций, где каждая функция принимает данные из предыдущего обработчика и передает данные в следующую функцию. Каждая такая функция и будет мидлварой. Самый простой пример — это вывод логов. Такая функция будет выводить в лог данные и передавать дальше, никак их не меняя:

Мидлвара redux-thunk добавляется в Redux и позволяет использовать асинхронный код внутри dispatch(). С ее помощью выносят логику выполнения запросов и обновления хранилища в отдельные функции (thunks). Вот пример такой функции:

Вообще thunk необязательно должен быть асинхронным. Thunk — это всего лишь функция, которая возвращает другую функцию и принимает dispatch и getState (при необходимости) в качестве параметров. Thunk может выполнять синхронные действия или комбинации синхронных и асинхронных операций, но в этом уроке нам это не так важно.

Код из примера выше можно реализовать и без redux-thunk, просто написав асинхронную функцию. Ей на вход мы передадим dispatch:

Разница проявляется в более продвинутых вариантах использования — например, когда мы работаем с состоянием или глобальными объектами. В этом случае не обойтись без redux-thunk:

Основное отличие здесь — это возможность передачи дополнительных параметров в thunk-функцию через extraArgument, а также быстро получить текущее состояние хранилища с помощью функции getState.

Механизм createAsyncThunk()

Несмотря на удобства redux-thunk, сами по себе thunks не уменьшают количество кода. Та же обработка ошибок всё еще составляет большую его часть. Здесь на помощь приходит инструмент createAsyncThunk(), появившийся вместе с redux-toolkit:

Каждый thunk, созданный через createAsyncThunk(), содержит внутри себя три события:

  • pending
  • fulfilled
  • rejected

Они соответствуют состояниям промиса и вызываются в Redux Toolkit в тот момент, когда промис переходит в одно из этих состояний. Нам не обязательно реагировать на все. Мы сами выбираем, что нам важно в приложении.

Применение thunk

Thunk выходит за рамки обработки асинхронных запросов. Этот механизм можно использовать в различных сценариях, где требуется вынос сложной логики или побочных эффектов из компонентов. Thunk также оказывается полезным для написания логики, зависящей от состояния в Redux. Ещё одним важным аспектом использования Thunk является возможность отправки нескольких действий в определенный момент или в течение заданного времени.

При этом Thunk дополняет, а не заменяет useEffect, который по-прежнему может использоваться для работы с побочными эффектами в компонентах. Если необходимо выделить какую-то логику из компонента, чтобы сделать его более универсальным для повторного использования, эту логику можно перенести в Thunk вместо использования useEffect.

На практике useEffect и createAsyncThunk часто используются вместе. Например, вызов dispatch(fetchData()) внутри useEffect с пустым массивом зависимостей позволяет подгрузить данные при инициализации компонента, обеспечивая эффективное взаимодействие между асинхронными операциями и жизненным циклом компонента.

Что дальше

В современной разработке вместе с React часто используется TypeScript. Вы можете познакомиться с ним в курсе Основы Typescript.