React删除列表复选框项时下方项异常自动选中问题排查
问题描述
以下是我的组件实现效果:
点击删除按钮后,被删除项下方的复选框会在从未被点击操作的情况下自动变为选中状态,属于不符合预期的异常行为:
我是React新手,以下是父组件与子组件的完整实现代码,请问该异常产生的原因是什么,该如何修复?
TaskList.js
import { useState, useRef } from "react"; import Task from "./Task"; function TaskList({ className, tasks, setTasks }) { let inputRef = useRef(); return ( <div className={className}> <ul className="grow overflow-y-scroll"> {tasks.map((task, i) => ( <Task description={task.description} onChange={() => { task.checked = !task.checked; }} /> ))} </ul> <input className="my-2 border-b-2 border-blue-300" placeholder="Enter task description..." ref={inputRef} ></input> <button className="rounded-md bg-blue-400 w-full p-4 text-white text-2xl" onClick={() => { setTasks([ ...tasks, { description: inputRef.current.value, checked: false }, ]); }} > Add </button> <button className="w-full bg-red-600 rounded-md p-2" onClick={() => { setTasks(tasks.filter((t) => !t.checked)); }} > Delete </button> </div> ); } export default TaskList;
Task.js
import React, { useState } from "react"; function Task({ description, onChange }) { return ( <li className="flex flex-row my-2"> <input className="mx-2" type="checkbox" onChange={onChange}></input> <span>{description}</span> </li> ); }
异常原因
异常由两个React基础使用错误共同导致:
- 列表渲染时没有给子项设置唯一稳定的
key属性,React默认按数组索引复用DOM节点。删除前面的列表项后,后面的DOM节点会被错位复用,DOM本身保存的checkbox选中状态会直接带到下一个渲染的项上。 - 直接修改state对象的属性(代码里直接写
task.checked = !task.checked),没有通过setTasks做不可变状态更新,React感知不到tasks状态的变化,不会主动同步checkbox的选中状态,UI完全依赖DOM自身的临时状态,进一步放大了DOM复用导致的错位问题。
修复方法
- 给每个任务项生成唯一稳定的id,列表渲染时用id作为
key,不要用数组索引当key - 所有对tasks的修改都走不可变更新,通过
setTasks返回全新的状态数组,不要直接修改原state上的属性 - 把checkbox改为受控组件,将checked状态从父组件传递给子组件,保证状态和UI完全对应
修复后的核心代码示例:
修复后的TaskList.js
import { useState, useRef } from "react"; import Task from "./Task"; function TaskList({ className, tasks, setTasks }) { let inputRef = useRef(); return ( <div className={className}> <ul className="grow overflow-y-scroll"> {tasks.map((task) => ( <Task key={task.id} description={task.description} checked={task.checked} onChange={() => { // 不可变更新选中状态 setTasks(tasks.map(t => t.id === task.id ? {...t, checked: !t.checked} : t)) }} /> ))} </ul> <input className="my-2 border-b-2 border-blue-300" placeholder="Enter task description..." ref={inputRef} ></input> <button className="rounded-md bg-blue-400 w-full p-4 text-white text-2xl" onClick={() => { if(!inputRef.current.value.trim()) return setTasks([ ...tasks, // 新增项时生成唯一id { id: Date.now() + Math.random(), description: inputRef.current.value.trim(), checked: false }, ]); inputRef.current.value = '' }} > Add </button> <button className="w-full bg-red-600 rounded-md p-2" onClick={() => { setTasks(tasks.filter((t) => !t.checked)); }} > Delete </button> </div> ); } export default TaskList;
修复后的Task.js
import React from "react"; function Task({ description, checked, onChange }) { return ( <li className="flex flex-row my-2"> {/* 受控绑定checked状态 */} <input className="mx-2" type="checkbox" checked={checked} onChange={onChange}></input> <span>{description}</span> </li> ); } export default Task;
内容的提问来源于stack exchange,提问作者JohnWick
相关产品推荐
相关产品推荐

