React map渲染单选按钮时如何将其他已选项状态置为false
问题原因
当前实现无法实现单选互斥的核心原因是每个EachData组件各自维护独立的isOpen内部状态,组件之间没有状态同步机制,选中新的项时旧项的内部状态不会自动重置,就会出现多个项同时为激活态的问题。
另外你的代码还存在一个传参bug:子组件声明接收的prop名为open,但父组件遍历渲染时传入的是isOpen={false},导致子组件初始化时拿不到传入的open值。
修复方案
单选互斥类的场景,需要把「当前激活项」的状态提升到父组件统一管理,子组件只负责接收状态、触发回调,不自行维护互斥相关的状态。
1. 修改父组件App.js
在父组件中存储当前激活的编辑行索引,统一控制所有行的展开/收起状态:
import { useState } from 'react'; import EachData from './EachData'; function App() { // 替换为你实际使用的表格数据源 const [tableData, setTableData] = useState([]); // 存储当前处于编辑激活状态的行索引,null表示无激活项 const [activeEditIndex, setActiveEditIndex] = useState(null); const handleToggleEdit = (targetIndex) => { // 点击已激活的项则关闭,否则激活当前点击项,其余项自动变为未激活 setActiveEditIndex(current => current === targetIndex ? null : targetIndex); }; return ( <table> <tbody> {tableData.map((data, index) => ( <EachData // 建议用数据的唯一id作为key,不要直接用index,避免数据变动时状态错乱 key={data.id || index} data={data} index={index} isOpen={activeEditIndex === index} onToggle={() => handleToggleEdit(index)} /> ))} </tbody> </table> ); } export default App;
2. 修改子组件EachData.jsx
移除子组件内部的独立状态,直接使用父组件传入的状态和回调即可:
function EachData({ data, index, isOpen, onToggle }) { return ( <tr> {/* 其余td内容保持原样 */} <td> <div className="functions"> {!isOpen ? ( <> <label className="far fa-edit" htmlFor={`label-${index + 1}`}> <input type="radio" name="edit" id={`label-${index + 1}`} checked={isOpen} onChange={onToggle} /> </label> <label className="far fa-trash"></label> </> ) : ( <> <label className="far fa-circle-check"></label> <label className="far fa-times-circle" htmlFor={`label-${index + 1}`}> <input type="radio" name="edit" id={`label-${index + 1}`} checked={isOpen} onChange={onToggle} /> </label> </> )} </div> </td> </tr> ); } export default EachData;
修复后效果
- 同一时间最多只有一行处于激活编辑状态,点击新的行会自动关闭之前激活的行
- 点击已激活行的关闭按钮,可回到所有行均未激活的初始状态
- 单选按钮的checked状态和React状态完全绑定,不会出现原生DOM状态和逻辑状态不一致的问题
内容的提问来源于stack exchange,提问作者Shubham Singhvi
相关产品推荐
相关产品推荐

