React: Redux Toolkit
Теория: Асинхронные запросы
Проблематика работы с асинхронными запросами
Одна из самых сложных задач в построении фронтенд-приложений – работа с внешними запросами. Трудности приходят с двух сторон.
С одной стороны, асинхронность сама по себе порождает неоднозначности, перестают работать стандартные механизмы. 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.




