为什么React重渲染时map渲染的列表复选框会被默认选中?
问题根源
- 列表渲染的key值错误使用数组索引:React依靠key识别列表元素,删除元素后数组下标重置,原本对应第2行的index变成1,React会误认为现在的第1行是之前的第1行,直接复用旧的DOM节点,导致旧节点的选中状态被遗留下来。
- 复选框是非受控组件:你没有给input绑定
checked属性,复选框的选中状态和toDeleteRow数组没有关联,DOM状态和业务状态脱节。 toDeleteRow没有作为响应式state管理:看起来你是直接修改普通数组而非用useState声明的状态,直接调用splice/push不会触发状态更新,数组的状态也不会和UI同步。- 删除逻辑存在bug:循环遍历toDeleteRow的时候每次都给newArray重新赋值,最终只过滤了toDeleteRow的最后一个ID,其他要删除的ID都没生效。
修复方案
1. 修正key值和复选框受控逻辑
把map的key改成唯一的ticket.Id,同时给input绑定checked属性,保证UI状态和业务状态一致:
<tbody> {ticketArray.map((ticket: any) => ( {/* key改用唯一的业务ID,不要用index */} <tr key={ticket.Id}> <td>{ticket.Id}</td> <td>{ticket.createdDate}</td> <td>PR</td> <td> <i className="icon-edit icon-large" title="Edit" onClick={handlePopupModifShow} /> </td> <td> <input {/* 绑定checked属性,和toDeleteRow的状态关联 */} checked={toDeleteRow.includes(ticket.Id)} onChange={() => { AddToRemove(ticket.Id) }} className="form-check-input" type="checkbox" /> </td> </tr> ))} </tbody>
2. 把toDeleteRow改成响应式state管理
用useState声明这个数组,修改时返回新数组避免直接篡改原状态:
// 声明响应式状态 const [toDeleteRow, setToDeleteRow] = useState<string[]>([]) const AddToRemove= (id: string) => { setToDeleteRow(prev => { if (prev.includes(id)) { // 返回过滤后的新数组,不要直接splice原数组 return prev.filter(item => item !== id) } else { // 返回拼接后的新数组 return [...prev, id] } }) }
3. 修复删除逻辑
不需要循环过滤,一次filter就能排除所有要删除的ID:
const Remove = () => { if (toDeleteRow.length > 0) { // 一次过滤所有不在toDeleteRow里的元素即可 const newArray = ticketArray.filter(obj => !toDeleteRow.includes(obj.Id)) setTicketArray(newArray) // 清空待删除数组 setToDeleteRow([]) } else { alert("Select at least one row") } }
内容的提问来源于stack exchange,提问作者totito88
相关产品推荐
相关产品推荐

