I have created a custom hook that dynamically detects and returns the system color theme. The custom hook correctly detects every change and sets the value accordingly. But the component that uses the custom hook always shows the initial value returned by the hook although it re-renders on every theme change.
I would really appreciate if someone can explain why this happens, and can suggest a suitable solution.
Thanks in advance.
useThemeDetector.js
import { useState, useEffect } from 'react';
const useThemeDetector = () => {
// media query
const mq = window.matchMedia("(prefers-color-scheme: dark)");
const [ theme, setTheme ] = useState(mq.matches ? 'dark' : 'light');
const themeListener = e => {
setTheme(
e.matches
? 'dark'
: 'light'
);
};
useEffect(() => {
mq.addListener(themeListener);
return () => { mq.removeListener(themeListener); };
}, [theme]);
// debug output, shows correct value
console.log(`theme: ${theme}, from hook`);
return theme;
};
export default useThemeDetector;
App.js
import Board from './components/Board';
import { useState } from 'react';
import { ThemeContext } from './Context';
import useThemeDetector from './customHooks/useThemeDetector';
const themes = {
'light': {
'bgColor': "#fff",
'fgColor': "#000"
},
'dark': {
'bgColor': "#282c34",
'fgColor': "#61dafb"
}
};
function App() {
const sysTheme = useThemeDetector();
const [ theme, setTheme ] = useState(sysTheme);
const [ bgColor, setBgColor ] = useState(themes[theme]['bgColor']);
const [ fgColor, setFgColor ] = useState(themes[theme]['fgColor']);
// debug output, shows initial value on every render
console.log(`theme: ${theme}, from App`);
const toogleTheme = () => {
if (theme === 'light') {
setTheme('dark');
setBgColor(themes['dark']['bgColor']);
setFgColor(themes['dark']['fgColor']);
}
else {
setTheme('light');
setBgColor(themes['light']['bgColor']);
setFgColor(themes['light']['fgColor']);
}
};
const style = {
// styles...
};
return (
<ThemeContext.Provider value={{ theme, toogleTheme }}>
<div
className="App"
style={style}
>
<Board />
</div>
</ThemeContext.Provider>
);
}
export default App;