React删除组件后未删组件状态异常问题求助
问题描述
开发React项目时遇到异常:通过deleteNote函数删除指定NoteComponent后,未被删除的组件标题和文本会变成被删组件的内容,但ID保持正确。例如删除标题为“2”的笔记后,标题为“3”的笔记会显示“2”的内容,刷新页面后才恢复正常。尝试重新请求所有笔记数据也未解决问题。
相关代码
父组件 NotesWorkflow
import React, { Component, ReactNode } from "react"; import { CurrentNote, Note } from "../../interfaces/Note.interface"; import { List } from "../../components/list/List"; import { ListProps } from "../../interfaces/list/ListProps.interface"; import { NoteComponents } from "../../services/createComponents"; import { DashboardState } from "../../interfaces/dashboard/DashboardState"; import { getAllMessages } from "../../services/requests"; import { NewNoteComponent } from "../../components/note/NewNoteComponent"; export class NotesWorkflow extends Component< {}, CurrentNote & DashboardState & { notes: Note[] } & { NewNoteComponentBlockOpened: boolean; } > { state = { notes: [], currentId: "", NewNoteComponentBlockOpened: false, }; componentDidMount = async () => { this.setState({ notes: await getAllMessages(), }); }; createNewComponent = async () => { this.setState({ NewNoteComponentBlockOpened: true, }); }; render(): React.ReactNode { const { notes } = this.state; return ( <div className="dashboard__panel"> <div className="dashboard__list"> <input type="text" className="dashboard__search" placeholder="🔍" /> <List notes={notes as ListProps[]} selectNote={(id) => { this.setState({ currentId: id }); }} currentId={this.state.currentId} /> </div> <div className="dashboard__notes__wrapper"> <button className="note__button__addNew" onClick={this.createNewComponent} > Create a Note </button> {this.state.NewNoteComponentBlockOpened ? ( <NewNoteComponent closeNote={() => { this.setState({ NewNoteComponentBlockOpened: false }); }} addNewNoteDashboard={async (note) => { this.setState(({ notes }) => { const oldNotesCopy = notes.slice(); oldNotesCopy.push(note); return { notes: oldNotesCopy }; }); }} /> ) : ( "" )} {NoteComponents(notes, { selectNote: (id) => { this.setState({ currentId: id }); }, deleteNote: (id: string) => { const filtered = this.state.notes.filter((v: Note) => { console.log(` ${v.id} =? ${id} = ${v.id === id}`); return v.id !== id; }); this.setState({ notes: filtered, currentId: "", }); }, currentId: this.state.currentId, })} </div> </div> ); } }
NoteComponents 函数
import { NoteComponent } from "../components/note/NoteComponent"; import { Note } from "../interfaces/Note.interface"; import { NoteProperties } from "../interfaces/noteComponent/NoteProperties.interface"; export const NoteComponents = ( notes: Note[], noteProperties: Omit<NoteProperties, "note"> ) => { return notes.map((v, i) => { return ( <NoteComponent key={i} selectNote={noteProperties.selectNote} currentId={noteProperties.currentId} note={v} deleteNote={noteProperties.deleteNote} /> ); }); };
NoteComponent 组件
import React, { Component, ReactDOM } from "react"; import { NoteProperties } from "../../interfaces/noteComponent/NoteProperties.interface"; import { Note, PrevState } from "../../interfaces/Note.interface"; import { patchNote, deleteCurrentNote } from "../../services/requests"; export class NoteComponent extends Component< NoteProperties, Omit<Note, "id"> & PrevState > { mainRef: React.RefObject<HTMLDivElement> = React.createRef<HTMLDivElement>(); state: Readonly<Note & PrevState> = { ...this.props.note, prevState: this.props.note, }; openNote = () => { const { id } = this.props.note; this.props.selectNote(id); this.mainRef.current?.classList.remove("closedClass"); }; closeNote = () => { this.props.selectNote(""); this.mainRef.current?.classList.add("closedClass"); }; updateNote = async () => { const note: Note = { id: this.props.note.id, text: this.state.text, title: this.state.title, }; const response: Note = await patchNote(note); const newState: Note & PrevState = { ...response, prevState: { ...response, }, }; this.setState(newState); this.closeNote(); }; deleteNote = async () => { const { id } = this.props.note; await deleteCurrentNote(id); this.props.deleteNote(id); }; handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => { event.preventDefault(); const { currentTarget } = event; const name = currentTarget.getAttribute("name"); if (name === "title") { this.setState({ title: currentTarget.value }); } else { this.setState({ text: currentTarget.value }); } }; render() { const { currentId, note } = this.props; const isOpened = currentId === note.id; const TextAreas = () => { if (!isOpened) { return ( <> <h3>{this.state.prevState.title}</h3> <p>{this.state.prevState.text}</p> </> ); } return ( <> <textarea name="title" className="note__title" value={this.state.title} onChange={this.handleChange} /> <textarea name="text" className="note__text" value={this.state.text} onChange={this.handleChange} /> </> ); }; return ( <div className={"note__wrapper " + (isOpened ? "openedClass" : "")}> <div className={"note " + (isOpened ? "" : "closedClass")} ref={this.mainRef} onClick={this.openNote} > <button className="note__close" onClick={this.closeNote}> x </button> <div className="note__content">{TextAreas()}</div> <div className="note__buttons"> <button onClick={this.updateNote}>SAVE</button> <button onClick={this.deleteNote}>DELETE</button> </div> </div> </div> ); } }
解决方案
1. 替换数组索引为唯一ID作为组件key
React依赖key识别列表组件,用数组索引当key时,删除元素后后续组件索引前移,React会复用原有组件而非重新渲染,导致组件内部state和新props不匹配。
修改NoteComponents函数中的key值:
// 原代码 key={i} // 修改为 key={v.id}
2. 监听props更新同步组件state
NoteComponent的初始state从props初始化,但父组件notes数组变化后,复用组件的props更新,内部state不会自动同步。添加componentDidUpdate钩子同步props到state:
在NoteComponent中添加:
componentDidUpdate(prevProps: NoteProperties) { if (prevProps.note.id !== this.props.note.id) { this.setState({ ...this.props.note, prevState: this.props.note }); } }
完成这两处修改后,删除笔记时React会正确识别组件,组件也会根据新props更新内部状态,不会再出现内容错乱问题。
内容的提问来源于stack exchange,提问作者Sulti
相关产品推荐
相关产品推荐

