React useState使用对象作为状态时切换主题仅前两次点击生效是什么原因
问题原因分析
- 你的
dark和light样式对象是直接定义在ParentElement组件内部的,React 组件每次重渲染时,整个组件函数会重新执行,这两个对象都会被重新创建,引用地址每次都不一样。 - 你在
changeTheme里用==判断currentTheme和light是否相等,本质是判断两个对象的引用地址是否一致,不是判断内容是否一致,这是逻辑失效的核心原因。
复现流程验证
- 初始渲染:生成
lightA对象,currentTheme初始值为lightA的引用,第一次点击判断lightA == lightA成立,设置主题为darkA,触发重渲染。 - 第二次渲染:生成全新的
lightB、darkB对象,currentTheme此时为darkA,第二次点击判断darkA == lightB不成立,设置主题为lightB,触发重渲染。 - 第三次及之后渲染:每次都会生成新的
lightX对象,currentTheme是上一轮渲染生成的旧light对象,和当前作用域下的新light对象引用永远不一致,所以判断永远走false分支,设置的主题永远是当前的新light对象,和旧light内容完全一致,视图看起来就不会再变化。
修复方案
方案1:把样式对象移到组件外部
对象只会创建一次,引用地址永远固定,原有判断逻辑就可以正常生效:
// 移到组件外,全局只初始化一次 const dark={ color:'white', backgroundColor:'darkgray', } const light={ color:'darkgray', backgroundColor:'white' } function ParentElement() { const [currentTheme,setTheme]=useState(light) function changeTheme(e){ currentTheme==light?setTheme(dark):setTheme(light) } return ( <div> <Button changeTheme={changeTheme}/> <Content style={currentTheme} /> </div> ) }
方案2:用字符串标识存储主题(更推荐)
彻底避免对象引用判断的问题,可维护性更高,后续新增主题也更方便:
const themeMap = { dark: { color:'white', backgroundColor:'darkgray', }, light: { color:'darkgray', backgroundColor:'white' } } function ParentElement() { // 存字符串标识,不是存对象 const [currentTheme,setTheme]=useState('light') function changeTheme(e){ setTheme(prev => prev === 'light' ? 'dark' : 'light') } return ( <div> <Button changeTheme={changeTheme}/> <Content style={themeMap[currentTheme]} /> </div> ) }
内容的提问来源于stack exchange,提问作者vinDev
相关产品推荐
相关产品推荐

