RXJSとメディアクエリを使用して観測可能な独自のダークモード検出を作成する


Demo Link
ブラウザーで利用できるより最近の特徴の1つは、する能力ですCSS Media Queries オペレーティングシステムのユーザーテーマとアクセシビリティ設定に基づきます@media (prefers-color-scheme: dark) (参照)prefers-color-scheme ) あなたは、ユーザーのOSのテーマは、現在ダークモードでは、これに応じてWebサイトのテーマを設定するために使用するかどうかを確認することができます.
このクエリは、window.matchMedia 関数-MediaListQuery これで2つのことができます.
  • ユーザー設定の現在の値matches ブールプロパティ
  • その未来を聞くことによるどんな将来価値でもchanges イベントリスター機能をイベントにアタッチします
  • これらを組み合わせることは、Aに変わる完璧な候補ですfully reactive dark mode switcher 使用RxJS そして、ユーザに現在の設定を与えるオブザーバブル.あなたがオブザーバブルに精通していないなら、彼らは時間とともに値を放出する一種のストリームです-消費者はこれらの観測値を彼らの価値を得るために購読することができます-これは我々が長い実行している機能またはイベントエミッタから値を得るためにそれらを使うことができることを意味します.
    完全なデモでは、あなた自身のOSの設定から設定された光と暗いモードでサンプルページを見つけることができます.完全な操作例を参照するには、設定を変更する必要があります(例えばOSXダークモードでは、“一般的な”設定の下にある)また、提供されているユーザートグルボタン、およびボタンをオフにし、メディアのクエリリスナーに提供されます.この例で観測可能なのは、複数のprefers- クエリの型ですが、以下のチュートリアルでは、より簡単に構築しますisDarkMode デモで提供されたものよりも観察可能ですが、コンセプトは同じです.

    暗いモード観測可能な作成


    我々のコードのために、我々は最初に我々の観測可能な工場をつくる必要があります--これは、我々が実装のためにどんな必須のパラメタでも通過するのを許して、それから購読されることができる観測可能なものを返す機能です.
    観測可能なコンストラクタは関数を受け取ります.新しいサブスクリプションがあるときはいつでもコールバックです.
    できるだけ早くサブスクリプションが開き、我々は最初にチェックするかどうかwindow.matchMedia 利用可能です-すべての近代的なブラウザで利用できるはずですが、ノードのような環境では利用できません( yay unit test ).ここでエラーをスローできます.
    このオプションは、AbortSignal , を含むオブジェクトonabort callback -シグナルの親
    エーAbortController そして、これを使用すると、外部のすべてのサブスクリプションを閉じ、すべてのイベントリスナーを削除することができます.
    コンストラクタの戻り値は別の関数です- teardownロジック-これは、rxjstakeUntil or take(1) - ここでは、すべてのサブスクリプションとイベントリスナーが閉じられていることを確認します.
    import { Observable } from 'rxjs';
    
    export function isDarkMode(signal?: AbortSignal): Observable<boolean> {
      return new Observable<boolean>(subscriber => {
    
        if (!window.matchMedia) {
          subscriber.error(new Error('No windows Media Match available'));
        }
    
        if (signal) {
          signal.onabort = () => {
            !subscriber.closed && subscriber.complete()
          }
        }
    
        return () => {
          !subscriber.closed && subscriber.complete()
        }
      });
    }
    

    メディアクエリの追加


    我々の観察可能な主な実装は、我々のMediaListQuery そして、任意の加入者に値を放出するために使用します.作成時にmatchestrue or false これはすぐにsubscriber.next .
    また、リスナーを使用してchange クエリのイベント.これを削除する必要があるので、後でイベントハンドラーの内部プライベート関数を作成しますsubscriber.next たびに検出された変更があります.
    また、イベントをMediaQueryListEvent タイプスクリプトがそれを認識するのを確実にしますmatches 値を含むプロパティ.
    function emitValue(event: Event) {
      subscriber.next((event as MediaQueryListEvent).matches);
    }
    
    const mediaListQuery = window.matchMedia('(prefers-color-scheme: dark)');
    mediaListQuery.addEventListener('change', emitValue);
    subscriber.next(mediaListQuery.matches);
    

    ハンドラーと購読をきれいにすること


    すでに新しい観測可能なものを使い始めることができますが、私たちはまた、次のことを確認する必要があります.
  • いずれかのときに観測可能な任意のサブスクリプションを終了しますAbortSignal 火災やRXJSからそれを取り消す
  • のDOM内のイベントリスナーを削除するchange イベント
  • リファクタリングの少しのビットで、我々は以下の我々の最終的な観測可能な工場を持ちますsignal.onabort
    観測可能なティアダウンロジックは、イベントリスナーを削除します
    我々の個人的な機能から.
    import { Observable } from 'rxjs';
    
    export function isDarkMode(signal?: AbortSignal): Observable<boolean> {
      return new Observable<boolean>(subscriber => {
    
        if (!window.matchMedia) {
          subscriber.error(new Error('No windows Media Match available'));
        }
    
        function emitValue(event: Event) {
          subscriber.next((event as MediaQueryListEvent).matches);
        }
    
        const mediaListQuery = window.matchMedia('(prefers-color-scheme: dark)');
    
        if (signal) {
          signal.onabort = () => {
            mediaListQuery.removeEventListener('change', emitValue)
            !subscriber.closed && subscriber.complete()
          }
        }
    
        mediaListQuery.addEventListener('change', emitValue);
        subscriber.next(mediaListQuery.matches);
    
        return () => {
          mediaListQuery.removeEventListener('change', emitValue);
          !subscriber.closed && subscriber.complete()
        }
      })
    }
    

    仕上げ


    今、私たちは、暗いモードのメディアクエリの完全に反応可能な観測可能な、これは、ユーザーのテーマ設定をチェックする任意のアプリケーションやウェブサイトで使用することができます.The demo これを完全に行う方法のもう一つの例を示します.
    isDarkMode().pipe(
      tap(value => {
        body.classList.removeClass(value ? 'light' : 'dark');
        body.classList.addClass(value ? 'dark' : 'light');
      })
    ).subscribe()
    
    このチュートリアルでは、RXJSで行うことができる種類の1つの小さな例です-時間をかけて値を発することができる任意のAPIはオブザーバブルに変換することができます.

    あなたのプロジェクトのための事前に構築された演算子とオブザーバブルのコレクション



    RxJS Ninja - のようなデータの様々な種類の作業のための130以上の演算子のコレクションですarrays , numbers and streams 変更、フィルタリング、データのクエリを許可します.
    まだアクティブな開発では、あなたのRXJSコードの明確な意図を提供する有用な演算子を見つけることがあります.
    ソースコードをチェックアウトできますGitHub .
    写真でLubo Minar on Unsplash