iT邦幫忙

2022 iThome 鐵人賽

DAY 29
0

成品

原始碼
展示

開發思路

參考 MDN 對於 window.matchMedia 的說明,可以知道這個方法會根據傳入的 media query 字串回傳對應的查詢結果:

The Window interface's matchMedia() method returns a new MediaQueryList object that can then be used to determine if the document matches the media query string, as well as to monitor the document to detect when it matches (or stops matching) that media query.

把查詢字串 (prefers-color-scheme: light) 傳入 window.matchMedia 後,結果可參考以下內容:

const queryResult = window.matchMedia('(prefers-color-scheme: light)');
console.info(queryResult);
// queryResult 內容如下:
{
  media: '(prefers-color-scheme: light)',
  matches: false,
  onchange: null
}

可以得知現在的系統顏色模式不是淺色模式。

而為了讓使用上更簡單,把以上邏輯打包成 custom hook 來使用,內容如下:

import { useCallback, useEffect, useState } from 'react';

type Scheme = 'light' | 'dark';

type PrefersColorScheme = `(prefers-color-scheme: ${Scheme})`;

const SCHEME_QUERY: PrefersColorScheme = '(prefers-color-scheme: light)';

export default function useSystemColorScheme(): Scheme {
  /* States */
  const [scheme, setScheme] = useState<Scheme>(
    window.matchMedia(SCHEME_QUERY).matches ? 'light' : 'dark'
  );

  /* Functions */
  const updateScheme = useCallback((e: MediaQueryListEvent): void => {
    if (e.matches) {
      setScheme('light');
    } else {
      setScheme('dark');
    }
  }, []);

  /* Hooks */
  useEffect(() => {
    const matchMedia = window.matchMedia(SCHEME_QUERY);
    matchMedia.addEventListener('change', updateScheme);
    return () => {
      matchMedia.removeEventListener('change', updateScheme);
    };
  }, [updateScheme]);

  /* Main */
  return scheme;
}

追加了 useEffect 讓 hook 能在系統的顏色模式改變時,回傳更新後的結果。

修改指南

另一種寫法是直接讓 hook 回傳「系統現在的顏色模式是否為淺(深)色」的布林值,原始碼調整如下:

import { useCallback, useEffect, useState } from 'react';

type Scheme = 'light' | 'dark';

type PrefersColorScheme = `(prefers-color-scheme: ${Scheme})`;

const SCHEME_QUERY: PrefersColorScheme = '(prefers-color-scheme: light)';

function useSystemIsLightMode(): boolean {
  /* States */
  const [isLightMode, setIsLightMode] = useState<boolean>(
    window.matchMedia(SCHEME_QUERY).matches
  );

  /* Functions */
  const updateScheme = useCallback((e: MediaQueryListEvent): void => {
    setIsLightMode(e.matches);
  }, []);

  /* Hooks */
  useEffect(() => {
    const matchMedia = window.matchMedia(SCHEME_QUERY);
    matchMedia.addEventListener('change', updateScheme);
    return () => {
      matchMedia.removeEventListener('change', updateScheme);
    };
  }, [updateScheme]);

  /* Main */
  return isLightMode;
}

要改為預設判定「是否為深色模式」的話就把 SCHEME_QUERY 的內容改為 '(prefers-color-scheme: dark)' 即可。

自評

邏輯本身簡單,搭配昨天介紹的 StyleProvider 元件就能做出「根據使用者的系統顏色模式,來預設要載入深色或是淺色的樣式」?

參考文件


上一篇
day28: StyleProvider
下一篇
結語:所以值得嗎?
系列文
我們可以不要 component library 了嗎?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言