React项目系统主题变更时Landing与Navbar组件图片不切换问题
问题描述
我开发的React网站已实现深色主题切换及系统主题检测功能,但存在以下问题:
- 当修改系统主题时,Landing和Navbar组件的图片无法同步切换;
- 通过
themeChange()函数手动切换主题时,这两个组件的图片能正常切换,且网站其他元素均可随系统主题变更正常切换。
Home.jsx 代码
function Home() { const [darkTheme, setDarkTheme] = useState() const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)').matches; useEffect(() => { if(prefersDarkScheme) { document.body.classList.toggle("light-theme"); setDarkTheme(prefersDarkScheme) } else { document.body.classList.toggle("dark-theme"); setDarkTheme(prefersDarkScheme) } }, []) function themeChange() { if(prefersDarkScheme) { document.body.classList.toggle("light-theme"); setDarkTheme(!darkTheme) } else { document.body.classList.toggle("dark-theme"); setDarkTheme(!darkTheme) } }; return( <div className="Home fade" id="home"> <Navbar themeChange={themeChange} darkTheme={darkTheme} /> <Landing darkTheme={darkTheme} /> <About /> <Skills /> <Projects /> <Contact /> <Footer /> </div> ) }
Landing组件代码(Navbar逻辑一致)
function Landing(props) { const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)').matches; const [darkNav, setDarkNav] = useState(props.darkTheme) useEffect(() => { if(props.darkTheme === true){ setDarkNav(true) } else if (props.darkTheme === false) { setDarkNav(false) } else { setDarkNav(prefersDarkScheme) } }, [props.darkTheme]) return( <div className='Landing-Page'> <div className='Landing-box'> <div className='Landing-Text'> <h1>Bridger Brown</h1> <h2>Front-End Web Developer + Designer</h2> </div> <div className='landpages'> { !darkLand ? <img src='./src/assets/wave-dark.jpg' className="fade" id='land-bg'/> : <img src='./src/assets/wave-light.jpg' className="fade" id='land-bg2'/> } </div> </div> </div> ) }
问题原因与修复方案
问题原因
- Home组件未监听系统主题变化:
prefersDarkScheme仅在组件初始化时获取一次值,当系统主题后续变更时,不会触发darkTheme状态更新,导致子组件Landing/Navbar接收的darkThemeprops没有变化,图片切换逻辑无法触发。 - Landing组件变量名错误:JSX中判断用的是
darkLand,但组件内定义的状态是darkNav,这会导致图片切换逻辑完全失效(即使props更新)。
修复步骤
1. 给Home组件添加系统主题监听
修改Home组件的useEffect,监听媒体查询的change事件,实时更新主题状态:
function Home() { const [darkTheme, setDarkTheme] = useState() // 获取媒体查询对象 const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)'); useEffect(() => { // 初始化主题 const updateTheme = (e) => { const isDark = e.matches; setDarkTheme(isDark); // 更新body类名 document.body.classList.toggle("dark-theme", isDark); document.body.classList.toggle("light-theme", !isDark); }; // 初始化执行一次 updateTheme(darkModeQuery); // 添加监听事件 darkModeQuery.addEventListener('change', updateTheme); // 清理函数 return () => darkModeQuery.removeEventListener('change', updateTheme); }, []) function themeChange() { // 手动切换时反转状态 const newDarkTheme = !darkTheme; setDarkTheme(newDarkTheme); document.body.classList.toggle("dark-theme", newDarkTheme); document.body.classList.toggle("light-theme", !newDarkTheme); }; return( <div className="Home fade" id="home"> <Navbar themeChange={themeChange} darkTheme={darkTheme} /> <Landing darkTheme={darkTheme} /> <About /> <Skills /> <Projects /> <Contact /> <Footer /> </div> ) }
2. 修复Landing组件的变量名错误
将JSX中的darkLand替换为darkNav,同时简化状态更新逻辑:
function Landing(props) { // 无需重复定义媒体查询,直接使用props传递的darkTheme即可 const [darkNav, setDarkNav] = useState(props.darkTheme) useEffect(() => { setDarkNav(props.darkTheme); }, [props.darkTheme]) return( <div className='Landing-Page'> <div className='Landing-box'> <div className='Landing-Text'> <h1>Bridger Brown</h1> <h2>Front-End Web Developer + Designer</h2> </div> <div className='landpages'> { !darkNav ? <img src='./src/assets/wave-dark.jpg' className="fade" id='land-bg'/> : <img src='./src/assets/wave-light.jpg' className="fade" id='land-bg2'/> } </div> </div> </div> ) }
3. 优化说明
- 统一用
document.body.classList.toggle(className, condition)的方式切换类名,逻辑更清晰,避免重复判断。 - 移除Landing组件中冗余的媒体查询,直接依赖父组件传递的
darkThemeprops,保持数据流向一致。 - 添加监听事件的清理函数,避免内存泄漏。
内容的提问来源于stack exchange,提问作者ILLUSORY
相关产品推荐
相关产品推荐

