Last active
February 24, 2023 17:20
-
-
Save gragland/cdaab58e8621be22301700e6a5d59498 to your computer and use it in GitHub Desktop.
React Hook recipe from https://usehooks.com. Demo: https://codesandbox.io/s/p33j1m0mxj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} |
This is small but will help users get in the right mindset of effect hooks. I think the comment // Only re-call effect when value changes
should say something to like // Sync this effect with the "enabled" state
. This comes with the explanation of the new mental model behind the effect hook by Ryan Florence - https://twitter.com/ryanflorence/status/1125041041063665666
This code:
if (enabled) {
element.classList.add(className);
} else {
element.classList.remove(className);
}
can be simplified to:
element.classList.toggle(className, enabled);
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@DimitarNestorov Thanks, changed it to
window
and moved intouseEffect
callback to avoid SSR issues withwindow
not being defined. What is the rationale behind wrapping the callback inrequestAnimationFrame
? Since this effect fires infrequently seems like an unnecessary performance optimization to me.