I got stuck at enabling dark mode for my react/typescript element. I created a Context.Provider to switch light/dark mode for the entire app, but the toggle does not work at all. If anybody knows how to fix it, please help.
This is the ThemeContext and ContextProvider
import { createContext, useState, useEffect } from 'react'
type ThemeName = 'light' | 'dark' | string
type ThemeContextType = {
theme: ThemeName
setTheme: (name: ThemeName) => void
}
const getInitialTheme = () => {
if (typeof window !== 'undefined' && window.localStorage) {
const storedPrefs = window.localStorage.getItem('color-theme')
if (typeof storedPrefs === 'string') {
return storedPrefs
}
const userMedia = window.matchMedia('(prefers-color-scheme:dark)')
if (userMedia.matches) {
return 'dark'
}
}
// returning default theme here
return 'light'
}
export const ThemeContext = createContext<ThemeContextType>({} as ThemeContextType)
export const ThemeProvider = ({ initialTheme, children }) => {
const [theme, setTheme] = useState(getInitialTheme)
const rawSetTheme = theme => {
//Updated rawSetTheme to theme above//
const root = window.document.documentElement
const isDark = theme === 'dark'
root.classList.remove(isDark ? 'light' : 'dark')
root.classList.add(theme)
localStorage.setItem('color-theme', theme)
}
if (initialTheme) {
rawSetTheme(initialTheme)
}
useEffect(() => {
rawSetTheme(theme)
}, [theme])
return <ThemeContext.Provider value={{ theme, setTheme }}>{children}</ThemeContext.Provider>
}
And this is the index.tsx.
ReactDOM.render(
<ThemeProvider>
<App />, document.getElementById('root')
</ThemeProvider>
)
And this is the Toggle
export const DarkModeToggle: VFC<Props> = memo(() => {
const { theme, setTheme } = useContext(ThemeContext)
function isDark() {
return theme === 'dark'
}
function toggleTheme(e) {
setTheme(e.target.checked ? 'dark' : 'light')
}
return (
<div className='flex flex-col'>
<label htmlFor='unchecked' className='mt-3 inline-flex items-center cursor-pointer'>
<span className='relative'>
<span className='block w-10 h-6 bg-gray-200 rounded-full shadow-inner'></span>
<span
className={`${
theme === 'dark' ? 'bg-indigo-400 transform translate-x-full' : 'bg-white'
} absolute block w-4 h-4 mt-1 ml-1 rounded-full shadow inset-y-0 left-0 focus-within:shadow-outline transition-transform duration-300 ease-in-out`}
>
<input
id='darkmode'
onClick={e => toggleTheme(e)}
type='checkbox'
checked={isDark()}
className='absolute opacity-0 w-0 h-0'
/>
</span>
</span>
<span className='ml-3 text-sm'>{theme === 'dark' ? 'ON' : 'OFF'}</span>
</label>
</div>
)
})
Updated: I changed the 'rawSetTheme' to 'theme' for variable, but it returns an error" in App.tsx as below. If you have any suggestions, it would be very appreciated.
Property 'initialTheme' is missing in type '{ children: Element; }' but required in type '{ initialTheme: any; children: any; }'. TS2741
7 | export default function App() {
8 | return (
> 9 | <ThemeProvider>
| ^
10 | <Router />
11 | </ThemeProvider>
12 | )
rawSetThemeis hit every time and the class values changes on the HTML tag and local storage.