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
相关产品推荐
相关产品推荐

