Instantly share code, notes, and snippets.

@gragland gragland/use-dark-mode.jsx
Last active Feb 19, 2019

Embed
What would you like to do?
// Usage
function App() {
const [darkMode, setDarkMode] = useDarkMode();
return (
<div>
<div className="navbar">
<Toggle darkMode={darkMode} setDarkMode={setDarkMode} />
</div>
<Content />
</div>
);
}
// Hook
function useDarkMode() {
// Use our useLocalStorage hook to persist state through a page refresh.
// Read the recipe for this hook to learn more: usehooks.com/useLocalStorage
const [enabledState, setEnabledState] = useLocalStorage('dark-mode-enabled');
// See if user has set a browser or OS preference for dark mode.
// The usePrefersDarkMode hook composes a useMedia hook (see code below).
const prefersDarkMode = usePrefersDarkMode();
// If enabledState is defined use it, otherwise fallback to prefersDarkMode.
// This allows user to override OS level setting on our website.
const enabled =
typeof enabledState !== 'undefined' ? enabledState : prefersDarkMode;
// Fire off effect that add/removes dark mode class
useEffect(
() => {
const className = 'dark-mode';
const element = window.document.body;
if (enabled) {
element.classList.add(className);
} else {
element.classList.remove(className);
}
},
[enabled] // Only re-call effect when value changes
);
// Return enabled state and setter
return [enabled, setEnabledState];
}
// Compose our useMedia hook to detect dark mode preference.
// The API for useMedia looks a bit weird, but that's because ...
// ... it was designed to support multiple media queries and return values.
// Thanks to hook composition we can hide away that extra complexity!
// Read the recipe for useMedia to learn more: usehooks.com/useMedia
function usePrefersDarkMode() {
return useMedia(['(prefers-color-scheme: dark)'], [true], false);
}
@dimitarnestorov

This comment has been minimized.

Copy link

dimitarnestorov commented Feb 19, 2019

You should wrap the useEffect callback in requestAnimationFrame

@dimitarnestorov

This comment has been minimized.

Copy link

dimitarnestorov commented Feb 19, 2019

global? Shouldn't that be window?

@gragland

This comment has been minimized.

Copy link
Owner Author

gragland commented Feb 19, 2019

@dimitarnestorov Thanks, changed it to window and moved into useEffect callback to avoid SSR issues with window not being defined. What is the rationale behind wrapping the callback in requestAnimationFrame? Since this effect fires infrequently seems like an unnecessary performance optimization to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment