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

如何为React CRUD表格添加撤销功能并修复删除操作时Toast通知不显示问题

Fixing Toast Notification & Adding Undo Functionality for Your CRUD Table

Let's work through your two issues step by step—getting the toast to show up, and adding that undo functionality similar to the example you referenced.


1. Fixing the Missing Toast Notification

The main problem here is where you've placed the toast() call: inside the filter callback. The filter method is meant to be a pure function (no side effects like showing toasts) that just decides which items to keep in the array. Your toast was getting lost in that logic, hence why it wasn't displaying.

Here's how to adjust the deleteCustomer function to trigger the toast correctly:

const deleteCustomer = (ID) => {
  if (window.confirm("Are you sure you want to delete?")) {
    Axios.delete(`http://localhost:3001/stats/delete/${ID}`).then(
      (response) => {
        // First, update the customer list
        setCustomerList(prevList => prevList.filter(val => val.ID !== ID));
        // Now trigger the toast outside the filter callback
        toast("Customer deleted!");
        console.log("toast notify run");
      }
    );
  }
};

This moves the toast to a spot where it runs right after the state is updated, ensuring it fires reliably.


2. Adding Undo Functionality

The core idea here is:

  • Keep a reference to the deleted customer so we can restore it
  • Show a toast with an undo button that gives the user a window to revert the action
  • Cancel the backend delete request if the user undoes (optional but clean)
  • Handle edge cases like failed backend requests

First, if you're not already using react-toastify (a common library for this pattern), install it:

npm install react-toastify
# or
yarn add react-toastify

Then import it in your component:

import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

Now update the deleteCustomer function with full undo logic:

const deleteCustomer = (ID) => {
  if (window.confirm("Are you sure you want to delete?")) {
    // 1. Grab the customer we're about to delete (for undo)
    const deletedCustomer = customerList.find(val => val.ID === ID);
    const deletedIndex = customerList.findIndex(val => val.ID === ID);

    // 2. Update the local list immediately for instant user feedback
    setCustomerList(prevList => prevList.filter(val => val.ID !== ID));

    // 3. Set up an abort controller to cancel the backend request if we undo
    const abortController = new AbortController();

    // 4. Send the delete request to the backend
    const deleteRequest = Axios.delete(`http://localhost:3001/stats/delete/${ID}`, {
      signal: abortController.signal
    });

    // 5. Show a toast with an undo button
    const toastId = toast("Customer deleted successfully!", {
      autoClose: 5000, // Auto-close after 5 seconds
      closeOnClick: false,
      draggable: false,
      render: ({ closeToast }) => (
        <div className="d-flex align-items-center gap-2">
          <p className="mb-0">Customer deleted successfully!</p>
          <button 
            className="btn btn-sm btn-secondary"
            onClick={() => {
              // Undo action: restore the customer to its original position
              setCustomerList(prevList => {
                const newList = [...prevList];
                newList.splice(deletedIndex, 0, deletedCustomer);
                return newList;
              });
              // Cancel the backend delete request
              abortController.abort("User clicked undo");
              // Close the toast
              closeToast();
            }}
          >
            Undo
          </button>
        </div>
      )
    });

    // 6. Handle backend request results
    deleteRequest.then(() => {
      console.log("Customer deleted from backend");
    }).catch(error => {
      // Only restore if the error isn't from us canceling the request
      if (error.name !== "CanceledError") {
        setCustomerList(prevList => {
          const newList = [...prevList];
          newList.splice(deletedIndex, 0, deletedCustomer);
          return newList;
        });
        toast.error("Delete failed—customer restored.", { toastId });
      }
    });
  }
};

Finally, add the <ToastContainer /> component to your JSX (usually at the root of your component) to render the toasts:

return (
  <div className="container">
    {/* Your table and other UI elements */}
    <table className="table">
      <thead>
        {/* Table headers */}
      </thead>
      <tbody>
        {displayCustomers}
      </tbody>
    </table>
    {/* Add this line to render toasts */}
    <ToastContainer position="bottom-right" />
  </div>
);

Key Notes:

  • We update the local state first so the user sees the delete happen instantly, even before the backend responds.
  • The undo button restores the customer to its original position in the list (instead of just appending it) for a better user experience.
  • The abort controller prevents unnecessary backend work if the user undoes the action quickly.
  • We handle failed backend requests by restoring the customer and showing an error toast.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 18:53:13