В середине декабря 2019 года меня попросили создать новое приложение React Native для компании, для которой я поддерживаю существующее приложение React Native, и я был очень рад это сделать, рассматривая это как средство для разработки новых мест и правильного выполнения задач из начало.
В рамках работ по обслуживанию я создавал библиотеку компонентов React для существующего нативного приложения, чтобы абстрагировать компоненты от логики приложения и сделать их повторно используемыми (если мы в конечном итоге создадим новое приложение, хорошее предвидение моего часть там!).
Создавая эту библиотеку компонентов, я использовал Expo как средство для ускорения разработки и обнаружил, что это потрясающий инструмент для React Native, поэтому я решил, что буду создавать этот новый проект с Expo, поскольку мне дали срок 2 месяца на создание приложения. .
Стек нового приложения состоял из:
- React - потому что React - отличная библиотека компонентов
- React Native. А как еще я мог бы создать приложение на React Native?
- Expo - предлагает потрясающую функциональность развертывания и отличный набор библиотек для ускорения разработки.
- Моя библиотека компонентов - чтобы повторно использовать как можно больше компонентов из существующего приложения React Native компании.
- Redux. Хотя люди утверждают, что React Hooks устраняет необходимость в нем, я считаю, что это все еще отличное решение для управления состоянием, и более распространено то, что React Hooks означают, что другие разработчики могут легко подобрать кодовую базу.
- Redux Saga - поскольку у существующего приложения было много побочных эффектов, и я собирался повторно использовать часть логики этого приложения.
- React Navigation - существующее приложение использовало его, и я собирался повторно использовать часть кода навигации.
Одним из преимуществ Expo, помимо потрясающих инструментов развертывания (подробнее об этом в следующей публикации в блоге), является то, что он также значительно упрощает работу с push-уведомлениями.
Вам все еще нужно пройти через обычные обручи с Apple, чтобы создать .p8
сертификат, но как только он будет загружен на серверы Expo, вы можете использовать простой вызов API для отправки уведомлений, при условии, что вы знаете push-токен Expo устройства для его отправки. .
Этот API очень удобен при тестировании или отладке вашего приложения, так как вы можете легко запускать уведомления с помощью запроса POST.
Я нашел это чрезвычайно полезным, когда представил в приложении категории уведомлений, поскольку идентификатор категории меняется между приложениями в Expo и автономными версиями.
redux-saga Основы
redux-saga - это «альтернативная модель побочных эффектов для приложений Redux», но это единственная модель побочных эффектов для Redux, которую я когда-либо знал! Хотя я считаю, что для обозначения этого типа абстракции обычно используется термин «эпос».
redux-saga предлагает набор функций для работы с хранилищем Redux и разветвления процессов, наиболее распространенными из которых являются:
put
- отправить действие в магазин ReduxtakeEvery
- Выполнять указанную функцию (задачу) каждый раз, когда действие добавляется в хранилище ReduxtakeLatest
- Выполнять указанную функцию каждый раз, когда действие добавляется в хранилище Redux, но если для этого действия уже запущена задача, убейте ее перед запуском новой.take
- Подождите, пока указанное действие будет добавлено в хранилище Redux, и сохраните это действие (если нам нужно)call
- Вызов функции (функция и ее аргументы являются параметрами, поэтомуfunction(arg1, arg2)
будетcall(function, arg1, arg2)
)fork
- Разветвляет процесс для указанной задачи (полезно для длительных задач), вы можете использоватьcancel
для отмены этой задачи
redux-saga использует функции генератора для обработки операций, которые ему необходимо выполнить, что, если резюмировать (плохо), означает, что он возвращает итератор с каждой операцией yield
ed, поэтому следующая операция может быть выполнена при вызове next()
.
Использование функций генератора позволяет невероятно легко протестировать функцию redux-saga, поскольку вы можете вызвать next()
и убедиться, что результат такой, как вы ожидаете.
Однако эти функции генератора не вызываются напрямую, а вместо этого вызываются функциями-наблюдателями, которые take
действия отправляются в хранилище Redux, а затем перебирают операции функции генератора с одной из этих операций, отправляющих действие в хранилище Redux для обновления состояния приложения на результат.
Обычный образец:
- Приложение отправляет действие «запрос» (например, форму отправки), которое принимается наблюдателем, который затем вызывает функцию генератора (сага).
- Затем сага выполнит ряд операций (например, вызов API для отправки данных формы на сервер), и в случае успеха
put
действие «ответ» или действие «ошибка» будет неудачным. - Затем редуктор приложения выполняет действие, отправленное сагой, и, следуя стандартному потоку Redux, обновляет состояние приложения.
Этот подход работает хорошо, так как вы можете модульно тестировать каждый его аспект:
- Действия можно проверить, чтобы убедиться, что они возвращают правильный объект.
- Редуктор можно протестировать, чтобы убедиться, что при обработке ответного действия состояние приложения обновляется правильно.
- Сагу можно протестировать, чтобы убедиться, что при успешном вызове API он отправляет действие ответа, а при вызове API, генерирующем исключение, он отправляет действие при ошибке.
- Вы можете использовать что-то вроде Enzyme, чтобы гарантировать, что при нажатии кнопки она отправляет действие запроса.
Итак, вы знаете, как работает redux-saga, теперь давайте рассмотрим, как работают push-уведомления в приложениях Expo.
Push-уведомления с помощью Expo
Чтобы обрабатывать push-уведомления в вашем приложении с помощью Expo, вам необходимо создать прослушиватель событий с помощью функции Expo Notifications.addListener
.
Обратный вызов этого прослушивателя событий при отправке уведомления получит следующий объект:
- удаленное: уведомление было удаленным или локальным?
- данные - объект данных, отправленный с уведомлением.
- actionId - действие, которое нажал пользователь (если используются категории).
- origin - используется, чтобы определить, было ли получено уведомление, когда приложение было открыто или нет.
- userText - если уведомление позволяет пользователю вводить данные, сохраняются введенные данные.
Слушатель событий хорошо работает для компонентов React, поскольку вы можете использовать ловушку useEffect
для настройки слушателя при первом монтировании компонента, а затем использовать функции, созданные useState
, для обновления состояния приложения при получении уведомления.
Однако все становится немного сложнее, если вы хотите выделить логику обработки уведомлений в отдельный процесс, работающий в фоновом режиме.
Обработка push-уведомлений с помощью redux-saga
Я принял решение абстрагироваться от логики push-уведомлений в приложении в сагу, поскольку я принял дизайнерское решение с базой кода, что все побочные эффекты (которые я считаю push-уведомлениями) должны обрабатываться через redux-saga, что означает что есть только одно место в кодовой базе, где любая логика, не связанная с отображением, находится.
Дизайнерское решение было принято на основе анализа существующего кода приложения, наличия в нем функций, вызывающих другие функции без обратных вызовов / обещаний / async await, и того, сколько проблем с ним должно было произойти.
Первый шаг к обработке push-уведомлений с помощью redux-saga - это знать, как работать со слушателями событий с помощью redux-saga - Event Channels.
Каналы событий используются по существу как оболочка для функции прослушивателя событий и предоставляют функцию emit
, которую можно использовать в обратном вызове прослушивателя событий для «отправки» на канал.
После того, как канал настроен, наблюдатель на канале может затем take
события, «отправленные» на канал, и обработать их в стандартном способе саги о сокращении, используя put
или call
.
Этот наблюдатель должен находиться в цикле while, чтобы он мог take
каждое событие, и из-за этого процесс-наблюдатель должен запускаться как отдельная задача через fork
, чтобы он не блокировал функцию, которая устанавливает канал событий.
Тестирование этого подхода
Как было сказано ранее, преимущество использования redux-saga состоит в том, что невероятно легко все протестировать, и каналы событий ничем не отличаются.
Перед тестированием канала событий вам сначала нужно найти способ вызова обратного вызова, переданного прослушивателю событий, который обертывает канал событий, поскольку я использую Jest для своего тестирования, я использовал jest.mock
, чтобы имитировать модуль expo
с объектом, который предоставляет Notifications.addListener
, а затем с помощью mockImplentation
сохраните обратный вызов в объект, который я могу вызвать позже.
После того, как вы сохранили обратный вызов, вы можете создать экземпляр канала события, который будет использоваться в обещании, которое будет разрешено после того, как обратный вызов будет вызван и каналу событий будет «отправлено» событие.
Как только это обещание будет разрешено, вы можете утверждать событие, которое было взято из канала событий.
Вот пример такого теста:
Затем вы можете протестировать сагу наблюдателя таким же образом, как и другие генераторы саги редукции, но без последнего утверждения, что генератор работает, поскольку цикл while
предотвращает это.
И вы можете протестировать сагу, которая создает канал событий, наблюдателя и fork
их, точно так же, как вы тестируете свои другие генераторы саги-редукции.
Резюме
Используя канал событий для управления прослушивателем push-уведомлений, используемым Expo, вы можете обрабатывать объекты push-уведомлений, как и любые другие действия в Redux.
Используя redux-saga для обработки и прослушивания push-уведомлений, вы можете создать простую в обслуживании и тестировании кодовую базу, которая играет ключевую роль в постоянно развивающемся мире React и React Native.