UNPKG

use-color-scheme

Version:

React Hook to get users prefered color scheme

57 lines (49 loc) 1.65 kB
import { useState, useEffect } from 'react'; const PREFERENCES = { DARK: 'dark', LIGHT: 'light', NONE: 'no-preference' }; const values = [PREFERENCES.DARK, PREFERENCES.LIGHT, PREFERENCES.NONE]; const makeQuery = pref => `(prefers-color-scheme: ${pref})`; const matchPreference = pref => window.matchMedia(makeQuery(pref)); const getPreference = preferences => preferences.map(value => ({ preference: value, matchMedia: matchPreference(value) })).filter(pref => pref.matchMedia.matches)[0]; const attachListener = (pref, setScheme) => { let unbind; const listener = () => { const newPref = getPreference(values); setScheme(newPref.preference); pref.matchMedia.removeListener(listener); // recursion // NOTE: we need to attach a new listener to ensure it fires on next change unbind = attachListener(newPref, setScheme); }; pref.matchMedia.addListener(listener); return () => { if (unbind) { unbind(); } else { pref.matchMedia.removeListener(listener); } }; }; // NOTE: outside hook to avoid this value recomputing const initialPreference = getPreference(values); const useColorScheme = () => { if (!('matchMedia' in window)) { // can not detect return { scheme: PREFERENCES.NONE }; } const [scheme, setScheme] = useState(initialPreference ? initialPreference.preference : PREFERENCES.NONE); useEffect(() => { if (!initialPreference) return; return attachListener(initialPreference, setScheme); }, []); return { scheme }; }; export { PREFERENCES, attachListener, getPreference, makeQuery, matchPreference, useColorScheme, values };