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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 05:29:51