В этой статье мы создадим простой компонент React многократного использования, который отслеживает «Состояние видимости страницы».
При создании веб-приложения вы можете столкнуться с ситуациями, когда вам нужно отслеживать текущее состояние видимости приложения. Возможно, вам потребуется воспроизвести / приостановить воспроизведение видео или анимационного эффекта, снизить производительность некоторых задач, требующих высокой производительности, или просто отслеживать поведение пользователя для аналитики в зависимости от того, активна вкладка браузера или нет.
Теперь эта функция кажется довольно простой для реализации, пока вы действительно не попытаетесь реализовать ее в первый раз. Оказывается, отследить, активно ли пользователь взаимодействует с веб-приложением или нет, может быть довольно сложно.
Существует API видимости страницы, который отлично работает в большинстве случаев, но не обрабатывает все возможные случаи неактивности вкладок браузера. API видимости страницы, который отправляет событие visibilitychange
, чтобы сообщить слушателям, что видимое состояние страницы изменилось или имеет некоторые нарушения. Он не запускает событие в некоторых случаях, даже когда окно или соответствующая вкладка браузера находится вне поля зрения или не в фокусе.
Чтобы справиться с некоторыми из этих крайних случаев, нам нужно использовать комбинацию прослушивателей событий focus
и blur
как в document
, так и в элементе window
. Подробное обсуждение этого вопроса вы можете найти здесь.
Мы реализуем логику обходного пути, описанную в упомянутом выше руководстве, в небольшом приложении React. Не волнуйтесь, вы сможете прочитать это позже - мы объясним каждый аспект логики, которую будем использовать.
Если вы хотите погрузиться в код или посмотреть демонстрацию, она доступна в Codesandbox, а также Github.
Начиная
Мы будем использовать Codesandbox для начальной загрузки нашего приложения React (вы также можете использовать create-response-app). Мы создадим небольшое приложение, в котором будет элемент HTML5 Video, который будет воспроизводиться только тогда, когда вкладка браузера находится в фокусе или активна, в противном случае она будет автоматически приостановлена. Мы используем видео, потому что оно упростит тестирование функциональности нашего приложения.
Начнем с создания простейшего элемента, то есть компонента Video
. Это будет простой компонент, который получит логические реквизиты с именем active
и строковые реквизиты с именем src
, которые будут содержать URL-адрес видео. Если active
props верен, мы будем воспроизводить видео. В противном случае мы приостановим его.
Мы создадим простой компонент класса React. Мы визуализируем простой элемент видео, в качестве источника которого задан URL-адрес, переданный с помощью src
props, и мы будем использовать новый ref
API React, чтобы прикрепить ref
к узлу DOM видео. Мы установим автоматическое воспроизведение видео, предполагая, что при запуске приложения страница будет активной.
Здесь следует отметить, что Safari теперь не позволяет автоматически воспроизводить мультимедийные элементы без взаимодействия с пользователем. Метод жизненного цикла componentDidUpdate
очень удобен для обработки побочных эффектов при изменении свойств компонента. Поэтому здесь мы будем использовать этот метод жизненного цикла для воспроизведения и приостановки видео на основе текущего значения this.props.active
.
Различия в префиксе поставщиков браузера очень неприятны при использовании определенных API, и API видимости страницы, безусловно, является одним из них. Поэтому мы создадим простую служебную функцию, которая будет обрабатывать эти различия и единообразно возвращать нам совместимый API на основе браузера пользователя. Мы создадим и экспортируем эту небольшую функцию из pageVisibilityUtils.js в каталог src.
В этой функции мы будем использовать простой поток управления на основе операторов if-else для возврата API, специфичного для браузера. Как видите, мы добавили префикс ms для Internet Explorer и префикс webkit для браузеров Webkit. Мы сохраним правильный API в строковых переменных hidden
и visibilityChange
и вернем их из функции в виде простого объекта. Наконец, мы экспортируем функцию.
Затем мы переходим к нашему основному компоненту. Мы инкапсулируем всю нашу логику отслеживания видимости страницы в повторно используемый компонент класса React, используя шаблон Render Props. Мы создадим компонент класса с именем VisibilityManager
. Этот компонент будет обрабатывать добавление и удаление всех прослушивателей событий на основе DOM.
Мы начнем с импорта созданной ранее служебной функции и вызова ее, чтобы получить правильные API-интерфейсы для конкретного браузера. Затем мы создадим компонент React и инициализируем его состояние с одним полем isVisible
, установленным на true
. Это логическое поле состояния будет отвечать за отображение состояния видимости нашей страницы.
В методе жизненного цикла компонента componentDidMount
мы присоединим прослушиватель событий к документу для события visibilitychange
с методом this.handleVisibilityChange
в качестве его обработчика. Мы также будем прикреплять прослушиватели событий для событий focus и blur к документу, а также к элементу окна. На этот раз мы установим this.forceVisibilityTrue
и this.forceVisibilityFalse
в качестве обработчиков событий фокуса и размытия соответственно.
Затем мы создадим handleVisibilityChange
метод, который будет принимать единственный аргумент forcedFlag
. Этот аргумент forceFlag
будет использоваться, чтобы определить, вызван ли метод из-за события visibilitychange
или из-за событий фокуса или размытия. Это так, потому что методы forceVisibilityTrue
и forceVisibilityFalse
ничего не делают, кроме как вызывают метод handleVisibilityChange
со значениями true и false для аргумента forcedFlag
.
Внутри метода handleVisibilityChange
мы сначала проверяем, является ли значение аргумента forcedFlag
логическим (это потому, что если он вызывается из обработчика события visibilitychange
, то переданный аргумент будет объектом SyntheticEvent) .
Если это логическое значение, мы проверяем, истинно оно или ложно. Когда это правда, мы вызываем метод setVisibility
с истиной, в противном случае мы вызываем метод с ложью в качестве аргумента. Метод setVisibility
использует метод this.setState
для обновления значения поля isVisible
в состоянии компонента.
Но если forcedFlag
не является логическим, мы проверяем значение скрытого атрибута в документе и соответственно вызываем метод setVisibility
. На этом завершается наша логика отслеживания состояния видимости страницы.
Чтобы сделать компонент многоразовым, мы используем шаблон Render Props. То есть вместо рендеринга компонента из метода render
мы вызываем this.props.children
как функцию с this.state.isVisible
.
Наконец, мы подключаем наше приложение React к модели DOM в файле index.js. Мы импортируем наши два компонента React VisibilityManager
и Video
и создаем небольшой функциональный компонент React App
, составляя их. Мы передаем функцию в качестве потомков компонента VisibilityManager
, который принимает isVisible
в качестве аргумента и передает его компоненту Video
в его операторе возврата. Мы также передаем URL-адрес видео в качестве src
свойства Video
компоненту. Вот как мы используем компонент на основе Render Props VisiblityManager
. Наконец, мы используем метод ReactDOM.render
для рендеринга нашего приложения React на узле DOM с идентификатором root.
Заключение
Современные API-интерфейсы браузеров становятся действительно мощными и могут использоваться для удивительных вещей. Большинство этих API-интерфейсов являются императивными по своей природе, и иногда бывает сложно работать с ними в декларативной парадигме React. Использование мощных шаблонов, таких как Render Props, для обертывания этих API в их собственные повторно используемые компоненты React может быть очень полезным.
Первоначально опубликовано на able.bio .