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

React删除条目后无需重载全页实现组件局部刷新方案

问题原因

你当前实现删除后视图不更新的核心逻辑问题非常明确:

  • 书籍列表的源状态myBooks定义在父组件App中,AllBooks组件只是接收这个状态作为props渲染
  • 删除操作执行后,你只更新了AllBooks组件内部的lastDeletedTitle状态,没有对父组件中存储的书籍列表源数据做任何修改,传入AllBooks的books props始终是初始加载的旧数据,自然不会触发视图重渲染。
实现方案

二选一即可,根据你的业务场景选择:

方案1:本地过滤更新(推荐,无额外网络请求,性能更好)

核心思路是删除接口请求成功后,直接在原有状态基础上过滤掉已删除的条目,不需要重新请求全量数据。

修改步骤

  • 在父组件App中定义删除后更新列表的方法,将该方法通过props传递给AllBooks组件
  • 删除接口调用成功后,调用父组件传入的更新方法,过滤掉已删除的书籍,React检测到状态变化会自动重渲染视图
  • 移除不必要的lastDeletedTitle状态,删除成功后直接触发toast提示即可

修改后代码

App.tsx

import React, { useLayoutEffect, useState } from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import { getAllBooks } from "./api_actions/api_calls";
import "./App.css";
import AllBooks from "./components/AllBooks";
import InsertBooks from "./components/InsertBook";
import { Book } from "./models/Book";

function App() {
    const [myBooks, setMyBooks] = useState<Book[]>([]);

    // 新增:删除书籍后更新本地列表状态
    const removeBookFromList = (deletedBookId: string | number) => {
        setMyBooks(prevBookList => prevBookList.filter(book => book.id !== deletedBookId));
    };

    useLayoutEffect(() => {
        getAllBooks().then((orders) => {
            setMyBooks(orders);
        });
    }, []);

    return (
        <div className="App">
            <header className="App-header">
                <BrowserRouter>
                    <Routes>
                        {/* 把更新方法透传给子组件 */}
                        <Route path="/" element={<AllBooks books={myBooks} onDeleteSuccess={removeBookFromList} />} />
                        <Route path="/add" element={<InsertBooks />} />
                    </Routes>
                </BrowserRouter>
            </header>
        </div>
    );
}

export default App;

AllBooks.tsx

import React from "react";
import { toast, ToastContainer } from "react-toastify";
import { deleteBook } from "../api_actions/api_calls";
import { Book } from "../models/Book";
import Card from "./Card";

// 扩展props类型定义
interface IAllBooksProps {
    books: Book[];
    onDeleteSuccess: (deletedBookId: string | number) => void;
}

const AllBooks: React.FC<IAllBooksProps> = (props) => {
    const handleDeleteBook = (targetBook: Book) => {
        console.log("Trying to delete...", targetBook.title);
        // 注意:用书籍唯一id作为删除参数,不要用title,避免重名书籍删错
        deleteBook(targetBook.id).then(() => {
            // 更新父组件的列表状态
            props.onDeleteSuccess(targetBook.id);
            // 直接弹出删除成功提示
            toast(`${targetBook.title} has been deleted!`);
        });
    };

    return (
        <>
            {props.books?.map((book) => {
                return <Card key={book.id} book={book} onDelete={() => handleDeleteBook(book)} />;
            })}
            <ToastContainer />
        </>
    );
};

export default AllBooks;

方案2:删除后重新拉取全量数据(适合多端数据同步场景)

如果你的应用存在多用户同时操作书籍数据的场景,需要保证前端展示和服务端数据完全一致,可以在删除成功后重新请求一次全量书籍列表覆盖原有状态。

修改步骤

  • 把App组件中拉取书籍列表的逻辑抽成独立可复用的方法
  • 将这个拉取方法通过props传给AllBooks组件
  • 删除接口请求成功后,直接调用拉取方法重新获取全量书籍数据即可

核心修改点

App.tsx中抽离拉取逻辑:

const fetchBookList = () => {
    getAllBooks().then((orders) => {
        setMyBooks(orders);
    });
};

useLayoutEffect(() => {
    fetchBookList();
}, []);

// 透传给子组件
<Route path="/" element={<AllBooks books={myBooks} refreshBookList={fetchBookList} />} />

AllBooks.tsx的删除回调中,删除成功后调用props.refreshBookList()即可。

注意事项
  • 所有和列表源数据相关的更新操作,最终都要调用源状态对应的set方法(也就是这里的setMyBooks)才能触发组件重渲染,子组件内部的独立状态修改不会影响父组件传入的props
  • 删除操作不要用书名作为唯一匹配条件,存在重名风险,必须使用后端返回的书籍唯一id

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 19:36:34