React Hooks实现点击行切换背景色仅单行高亮方案
问题背景
接触React JS时间较短,需要实现列表行点击背景色切换效果,交互要求如下:
- 点击单行时该行背景变为红色,再次点击同一行则背景恢复白色
- 点击新行时,其余所有行自动重置为默认白色背景,始终最多只有一行是红色状态
当前编写的代码可运行,但存在点击多行时多行同时变红的异常,不符合预期。原有问题代码如下:
import { useState } from "react"; export default function App() { const [color, setColor] = useState("white"); const handleClick = (e) => { const value_color = e.target.style.backgroundColor; if (value_color != "" && value_color != undefined) { if (value_color === "white") var new_color = "red"; else var new_color = "white"; } setColor("white"); e.target.style.backgroundColor = new_color; }; return ( <div> <div id={"line1_id"} style={{ backgroundColor: color }} onClick={handleClick} > First Line </div> <div id={"line2_id"} style={{ backgroundColor: color }} onClick={handleClick} > Second Line </div> </div> ); }
问题根因
原有代码的问题出在两个地方:
- 违反React 状态驱动视图的核心原则,直接通过
e.target.style手动修改DOM样式,这部分修改不受React状态管控,状态触发重渲染时不会清理手动添加的样式,自然会出现多行红色残留 - 只用了一个全局
color状态绑定所有行的背景色,没有记录当前选中行的唯一标识,无法区分不同行的选中状态
修复实现
不要手动操作DOM,用单一状态记录当前激活行的唯一ID即可,修复后代码:
import { useState } from "react"; export default function App() { // 存储当前激活行的ID,null表示无选中行 const [activeLineId, setActiveLineId] = useState(null); const handleClick = (lineId) => { // 点击已选中行则取消选中,所有行回到白色 if (activeLineId === lineId) { setActiveLineId(null); return; } // 点击新行则更新激活ID,其余行自动重置为白色 setActiveLineId(lineId); }; return ( <div> <div id="line1_id" style={{ backgroundColor: activeLineId === "line1_id" ? "red" : "white" }} onClick={() => handleClick("line1_id")} > First Line </div> <div id="line2_id" style={{ backgroundColor: activeLineId === "line2_id" ? "red" : "white" }} onClick={() => handleClick("line2_id")} > Second Line </div> </div> ); }
逻辑说明
- 所有样式完全由状态驱动,没有任何手动DOM操作,不存在样式残留问题
- 重渲染时会逐行判断当前行ID是否和激活ID匹配,匹配则显示红色,否则显示白色,天然满足“点击新行其余行自动变白”的要求
- 点击已选中的行时,将激活ID设为
null,即可实现再次点击取消选中、背景恢复白色的效果 - 后续新增行只需要分配唯一ID、复用相同的判断逻辑即可,扩展性更好
内容的提问来源于stack exchange,提问作者init
相关产品推荐
相关产品推荐

