Поскольку вы создаете приложение React Native на нескольких платформах, одно из ключевых различий между вашими приложениями для iPhone и Android заключается в том, что в телефонах Android есть отдельная системная кнопка возврата, которую пользователи могут нажимать в любое время.

По мере того, как пользователь Android перемещается по вашему приложению и углубляется в новые сцены, ожидаемое поведение заключается в том, что нажатие на кнопку «Назад» возвращает пользователя к предыдущему экрану, если только он не находится на первом экране, где нажатие на спину Кнопка означает, что они выходят из приложения.

Возьмите это из Руководства разработчика Android:

Системная кнопка «Назад» используется для навигации в обратном хронологическом порядке по истории экранов, с которыми пользователь недавно работал. Обычно он основан на временных отношениях между экранами, а не на иерархии приложения.

React Native и кнопка возврата Android

Поведение React Native по умолчанию для кнопки «Назад» заключается в том, что нажатие кнопки приводит к выходу из приложения.

React Native предоставляет модуль BackAndroid, который позволяет разработчикам управлять поведением кнопки Назад. Этот модуль отвечает за регистрацию обратных вызовов, чтобы решить, что делать при нажатии кнопки возврата. Когда пользователь нажимает кнопку Назад, приложение просматривает все зарегистрированные прослушиватели событий и останавливается только в том случае, если один обратный вызов возвращает true. Если обратные вызовы не возвращают true, нажатие кнопки закрывает ваше приложение.

Чтобы реализовать обратную хронологическую навигацию по экрану с помощью кнопки «Назад», как описано, например, в Руководстве разработчика Android, разработчик может написать обратный вызов, который проверяет текущее количество маршрутов, прежде чем решать, выходить из приложения или нет:

Это работает достаточно хорошо, если эта функция является единственным зарегистрированным обратным вызовом. Но что происходит, когда вам нужно добавлять все больше и больше прослушивателей событий, чтобы охватить бизнес-логику для различных вариантов использования? В конце концов, в том же Руководстве разработчика Android написано, что кнопку Назад можно использовать для закрытия диалогов и всплывающих окон или для закрытия контекстных панелей действий.

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

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

Кроме того, если вы забудете удалить слушателя, это может привести к странным ошибкам! Без достаточного защитного кодирования вы можете в конечном итоге выполнить нажатие кнопки «Назад» для функции отправки кредитной карты, даже если это представление больше не отображается.

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

Побочный эффект реакции

Обработка поведения кнопки Назад - идеальный вариант использования библиотеки React Side Effect Дэна Абрамова. React Side Effect позволяет программистам создавать специальные компоненты более высокого порядка, которые предназначены для выполнения побочных эффектов, когда они отображаются и обновляются вашим приложением.

Одним из популярных пакетов, реализованных с помощью React Side Effect, является React Document Title, который позволяет программистам изменять заголовок окна браузера, передавая свойство компоненту <DocumentTitle>, например:

React Side Effect соответствует компонентной архитектуре React, поскольку он позволяет обертывать императивные API (например, BackAndroid React Native или изменять заголовок документа) в компоненты, которые лучше соответствуют модели программирования и абстракциям React.

Основная идея React Side Effect - создать компонент React, который принимает любые произвольные параметры, которые вы определяете. Затем этот компонент оборачивается функцией withSideEffect и двумя или тремя дополнительными функциями, которые вы реализуете. Эти функции вызываются одна за другой каждый раз, когда ваше приложение выполняет повторную визуализацию, а затем генерирует побочный эффект на основе предоставленной вами логики. Вот их обычные названия и поведение:

  • reducePropsToState - аргумент, переданный этой функции, представляет собой массив свойств для всех подключенных экземпляров вашего компонента.
  • handleStateChangeOnClient - эта функция вызывается с возвращаемым значением reducePropsToState и используется для создания побочного эффекта.
  • mapStateOnServer (необязательно) - Эта функция ведет себя как handleStateChangeOnClient, но вызывается на сервере, если ваше приложение React использует серверный рендеринг

Простая реализация DocumentTitle может выглядеть так:

Программисты могут реализовать эти функции любым способом по своему усмотрению: некоторые варианты использования могут позволять агрегировать свойства многих компонентов вместе в одно значение. Другие варианты использования, такие как этот, касаются только значения, предоставленного последнему компоненту.

В любом случае возвращаемое значение reducePropsToState передается как аргумент handleStateChangeOnClient, где мы используем этот аргумент для изменения заголовка документа.

Реализация желаемого поведения Android

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

Это реализуется за счет того, что в библиотеке BackAndroid React Native регистрируется только один обратный вызов. Этот обратный вызов, в свою очередь, вызовет функцию, предоставленную самому внутреннему экземпляру AndroidBackButton. Мы будем использовать React Side Effect для обновления функции, вызываемой внутри слушателя событий.

Если вы попробуете использовать этот код как есть в своем приложении, вы можете быть удивлены, обнаружив, что он не работает полностью. А именно, функция reducePropsToState выполняется, а handleStateChangeOnClient - нет. Почему нет?

Ответ связан со средой выполнения React Native и способом, которым React Side Effect определяет, как вызывать функции. Поскольку React Side Effect может работать как на клиенте, так и на сервере, библиотека проверяет, в какой среде она находится, спрашивая, есть ли у нее доступ к DOM, когда пришло время внести изменения.

React Native, как мы знаем, не отображает DOM! Поскольку среда React Native не удовлетворяет условиям этой проверки, мы не можем поместить нашу функцию в качестве второго handleStateClientOnClient аргумента, а должны вместо этого позиционировать ее как необязательный третий аргумент с довольно сбивающим с толку (для нас) именем mapStateOnServer.

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

Имея рабочую версию нашего компонента, мы теперь можем управлять поведением кнопки «Назад», отображая компонент в любом месте иерархии представлений нашего приложения. Мы можем определить даже базовое поведение (например, переход назад, если пользователь не находится на первом экране вашего приложения), а затем переопределить это поведение в определенных контекстах. И все это без ручного манипулирования сложением и вычитанием слушателей событий!

В заключение

Надеюсь, вы узнали кое-что о побочном эффекте React и о том, как компоненты более высокого порядка могут позволить вам воспользоваться абстракцией компонентов React для обертывания императивных API и работы с изменяющимися данными в ваших программах.

Я открываю исходный код этого компонента как react-native-android-back-button. Просмотрите исходный код здесь, установите его с помощью npm install react-native-android-back-button и дайте мне знать, что вы думаете!