В этой статье мы создадим простой компонент 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 .