Creé un enlace personalizado que detecta y devuelve dinámicamente el tema de color del sistema. El enlace personalizado detecta correctamente cada cambio y establece el valor en consecuencia. Pero el componente que usa el enlace personalizado siempre muestra el valor inicial devuelto por el enlace, aunque se vuelve a representar en cada cambio de tema.
Realmente agradecería si alguien puede explicar por qué sucede esto y puede sugerir una solución adecuada.
Gracias por adelantado.
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;Aplicación.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;El problema es que está usando useState dos veces. Ya existe una variable de estado dentro del gancho personalizado:
const [ theme, setTheme ] = useState(mq.matches ? 'dark' : 'light'); Pero luego, en App.js , está agregando otra variable de estado distinta:
const [ theme, setTheme ] = useState(sysTheme); Esa segunda variable es diferente a la que está dentro del enlace personalizado, por lo que simplemente se inicializa con el valor que tenía sysTheme cuando se inicializó.
En su lugar, puedes hacer algo como esto:
const [ theme, setTheme ] = useState(sysTheme);[theme, setTheme]App.js haz const [theme, setTheme] = useThemeDetector(); ...y deje el resto del código como está, ya que se refiere a esos nombres. Sin embargo, puede haber otros problemas con su gancho personalizado. Parece que agregará más y más detectores de eventos cada vez que cambie el valor del theme . Probablemente solo debería agregar un detector de eventos si ya no hay ninguno. No estoy 100% seguro de esto, pero sin duda lo probaría si fuera tú.
ACTUALIZACIÓN: Creo que deberías hacer que tu gancho useEffect dependa de setTheme y no de theme .