use-color-scheme
Version:
React Hook to get users prefered color scheme
57 lines (49 loc) • 1.65 kB
JavaScript
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 };