В середине декабря 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 - отправить действие в магазин Redux
  • takeEvery - Выполнять указанную функцию (задачу) каждый раз, когда действие добавляется в хранилище Redux
  • takeLatest - Выполнять указанную функцию каждый раз, когда действие добавляется в хранилище Redux, но если для этого действия уже запущена задача, убейте ее перед запуском новой.
  • take - Подождите, пока указанное действие будет добавлено в хранилище Redux, и сохраните это действие (если нам нужно)
  • call - Вызов функции (функция и ее аргументы являются параметрами, поэтому function(arg1, arg2) будет call(function, arg1, arg2))
  • fork - Разветвляет процесс для указанной задачи (полезно для длительных задач), вы можете использовать cancel для отмены этой задачи

redux-saga использует функции генератора для обработки операций, которые ему необходимо выполнить, что, если резюмировать (плохо), означает, что он возвращает итератор с каждой операцией yield ed, поэтому следующая операция может быть выполнена при вызове next().

Использование функций генератора позволяет невероятно легко протестировать функцию redux-saga, поскольку вы можете вызвать next() и убедиться, что результат такой, как вы ожидаете.

Однако эти функции генератора не вызываются напрямую, а вместо этого вызываются функциями-наблюдателями, которые take действия отправляются в хранилище Redux, а затем перебирают операции функции генератора с одной из этих операций, отправляющих действие в хранилище Redux для обновления состояния приложения на результат.

Обычный образец:

  1. Приложение отправляет действие «запрос» (например, форму отправки), которое принимается наблюдателем, который затем вызывает функцию генератора (сага).
  2. Затем сага выполнит ряд операций (например, вызов API для отправки данных формы на сервер), и в случае успеха put действие «ответ» или действие «ошибка» будет неудачным.
  3. Затем редуктор приложения выполняет действие, отправленное сагой, и, следуя стандартному потоку 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.