LoginSignup
0
0

More than 1 year has passed since last update.

Page Visibility APIでFirebaseのRealtime DatabaseのListenerを自動的に取り外す方法

Last updated at Posted at 2022-06-17

はいさい、ちゅらデータぬオースティンやいびーん!

概要

FirebaseのPWAでonValueなどのListenerを使っている時に、ユーザーが画面を見ていない時にそのListenerを取り外す方法を紹介します。

なぜ取り外すべきか?

PWAでは、アプリのように動くことを想定して作ります。そして、基本的に携帯・スマホをベースに考えてコードを書いていくものなのですが、スマホアプリ開発で破ってはならぬ鉄則があります。

バッテリー使用を抑えろ。

という鉄則です。
そこで、スマホアプリだと、ページが見えていない時はService Worker以外、何もしないようにしたいですね。

FirebaseのRealtime Database - onValueのListenerについて

FirebaseのJavaScript SDKには、Realtime Databaseのとあるところのデータ変更を全てリアルタイムで監視するonValueという関数があります。以下のような使い方をします。

import { getDatabase, ref, onValue } from "firebase/database";

const db = getDatabase() // initializeAppで作ったappをここに入れることを勧めます。
const chatRef = ref(db, "/chat"); // ユーザーのチャットルームなどのメッセージが入っている場所

onValue(chatRef, (allMessages) => {
  const data = allMessages.val();
  updateDOM(data); // DOMを更新する関数
}, (error) => console.error(error));

非常に便利な関数ですが、気にするべきことが多々あります。主に以下の3点。

  • 入ってくるデータが大量になり、アプリのメモリ使用が膨大になりうる
  • 常にchatRefの場所を監視しているので、バッテリー使用が大
  • スマホのSuspendと相性が悪く、そのままにすると、Suspendから戻ったページ・アプリがDBから情報を取得しなくなることがある。

onValueのListenerを取り外す方法

本題の解決に入る前に、onValueのListenerを外す方法をあらかじめ紹介しておきたいです。
以下のように、onValueが返す関数を定数・変数の保存しておけば、Listenerを取り外すことができます。

import { getDatabase, ref, onValue } from "firebase/database";

const db = getDatabase() // initializeAppで作ったappをここに入れることを勧めます。
const chatRef = ref(db, "/chat"); // ユーザーのチャットルームなどのメッセージが入っている場所

const disconnect = onValue(chatRef, (allMessages) => {
  const data = allMessages.val();
  updateDOM(data); // DOMを更新する関数
}, (error) => console.error(error));

disconnect(); // onValueのListenerが止まり、なくなる

解決法:Page Visibility API

上記の問題を解決するために、Page Visibility APIを使用することができます。
以下のようにdocumentにEventListenerを追加します。

document.addEventListener("visibilitychange", () => {
  const visibilityState = document.visibilityState; // "hidden" | "visible";
});

onValueと一緒に使えば、以下のような仕組みになります。

import { getDatabase, ref, onValue } from "firebase/database";

const db = getDatabase() // initializeAppで作ったappをここに入れることを勧めます。
const chatRef = ref(db, "/chat"); // ユーザーのチャットルームなどのメッセージが入っている場所

let disconnect = () => {} // ここに最新のonValueのListenerを取り外す関数を入れておく
const establishDatabaseConnection = () => {
  disconnect = onValue(chatRef, (allMessages) => {
    const data = allMessages.val();
    updateDOM(data); // DOMを更新する関数
  }, (error) => console.error(error));
}

establishDatabaseConnection(); // 初回

document.addEventListener("visibilitychange", () => {
  const visibilityState = document.visibilityState; // "hidden" | "visible";
  if (visibilityState = "visible") establishDatabaseConnection(); //  ユーザーが戻ったときにListenerを付け直す
  if (visibilityState = "hidden") disconnect(); // onValueのListenerが止まり、なくなる
});

これで、ユーザーが見ていない時にむやみにバッテリーを使わないようにできます!

ボーナス:Web Component APIとの円滑の組み込み方

Web ComponentsでFirebaseアプリを作るのはおすすめです!相性抜群です。
上記の仕組みを綺麗にWeb Componentのclassでまとめることができます。

import { getDatabase, ref, onValue } from "firebase/database";

class ChatBox extends HTMLElement {
  #disconnect;
  #chatRef;

  constructor() {
    super();
    this.#disconnect = () => {};
    const db = getDatabase();
    this.#chatRef = ref(db, "/chat");
  }

  connectedCallback() {
    this.#establishDatabaseConnection(); // Web ComponentがDOMに入った時に一度実行される
    document.addEventListener("visibilitychange", this.#handleVisibilityChange);
  }

  disconnectedCallback() {
    this.#disconnect(); // Web ComponentがDOMから消えた時に実行される
    document.removeEventListener("visibilitychange", this.#handleVisibilityChange);
  }

  #handleVisibilityChange = () => {
    const visibilityState = document.visibilityState;
    if (visibilityState = "visible") establishDatabaseConnection();
    if (visibilityState = "hidden") disconnect();
  });

  #establishDatabaseConnection() {
    this.#disconnect = onValue(this.#chatRef, (allMessages) => {
      const data = allMessages.val();
      this.#updateDOM(data);
    }, (error) => console.error(error));
  }

  #updateDOM() {...}
}

#updateDOMのところに、以前の記事で紹介したLit-htmlを使うことをお勧めします!

まとめ

以上、Page Visibility APIでonValueの悪い側面を少し減らす方法を紹介しました!

筆者の推測ですが、onValueでListenerをつけて、disconnectで取り外すのにもバッテリー使用が予想されるので、もし頻繁に開けては閉じるようなアプリ(どんなアプリかな?想像がつかん)を開発されているのであれば、このような仕組みは不要なのかもしれません。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0