You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React未调用setState时state复用已删除项状态问题(已解决)

React笔记应用删除功能状态异常问题

功能逻辑说明

应用内置删除按钮与用户删除确认状态逻辑:当用户确认删除操作后,isConfirmed 状态会更新为true,随后从MongoAtlas中删除对应数据条目,并移除App.jsx文件内notes数组中的对应笔记项。

异常表现

被删除笔记在数组中原索引位置的后续笔记(异常由App.jsx中 notes.map() 列表遍历逻辑引发),在未调用 setState 的情况下,isConfirmed 状态被自动设置为true,导致该笔记的删除按钮功能失效,必须刷新页面才能恢复正常使用。

关联代码片段

DeletePopup组件

function DeletePopup(props) {
const mountedRef = useRef();  //used to stop useEffect call on first render

const [isConfirmed, setIsConfirmed] = useState(false);
const [show, setShow] = useState(false);


function confirmDelete() {
    // console.log("user clicked confirm");
    setIsConfirmed(true);
    // console.log(isConfirmed);
    handleClose();
}

useEffect(() => {
    // console.log("delete useEffect() run");

    if (mountedRef.current) {
        props.deleteNote(isConfirmed);
    }

    mountedRef.current = true;
}, [isConfirmed]);

Note组件

function Note(props) {

function deleteNote(isConfirmed) {
    props.deleteNote(props.id, { title: props.title, content: props.content }, isConfirmed);
    console.log("note.deleteNote ran with confirmation boolean: " + isConfirmed);
}

return <Draggable
    disabled={dragDisabled}
    onStop={finishDrag}
    defaultPosition={{ x: props.xPos, y: props.yPos }}
>
    <div className='note'>

        <h1>{props.title}</h1>
        <p>{props.content}</p>

        <button onClick={handleClick}>
            {dragDisabled ? <LockIcon /> : <LockOpenIcon />}
        </button>
        <EditPopup title={props.title} content={props.content} editNote={editNote} />
        <DeletePopup deleteNote={deleteNote} />

    </div>
</Draggable>
}

App组件

function App() {
const [notes, setNotes] = useState([]);

function deleteNote(id, deleteNote, isConfirmed) {
    if (!isConfirmed) return;

    axios.post("/api/note/delete", deleteNote)
        .then((res) => setNotes(() => {
            return notes.filter((note, index) => {
                return id !== index;
            });
        }))
        .catch((err) => console.log(err));
}

return (
    <div id="bootstrap-override">
        <Header />
        <CreateArea
            AddNote={AddNote}
        />

        {notes.map((note, index) => {
            return <Note 
                key={index}
                id={index}
                title={note.title}
                content={note.content}
                xPos={note.xPos}
                yPos={note.yPos}
                deleteNote={deleteNote}
                editNote={editNote}
            />
        })}

        <Footer />
    </div>);
}

问题根因

列表渲染时错误使用数组索引作为key和笔记唯一标识:React做列表diff时,若以索引为key,删除中间项后后续列表项的索引会前移,React会直接复用原索引位置的组件实例,不会重新初始化组件内部状态。原本被删除笔记的DeletePopup组件中isConfirmed=true的状态,被直接复用到前移到该位置的新笔记组件上,触发了无操作下状态自动变更、删除按钮失效的问题。

修复方案

弃用数组索引作为列表项key和笔记唯一标识,改用MongoAtlas为每条笔记生成的稳定唯一Object ID作为key和id属性,删除笔记时也基于该稳定ID做数组过滤,避免组件实例错误复用。

修复验证:将Note组件的ID改为使用MongoAtlas生成的Object ID后,该异常已完全修复。

内容的提问来源于stack exchange,提问作者Aaron Lam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 03:27:22