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

TanStack React Table实现确认删除状态并在useEffect中删除记录

TanStack React Table 实现确认删除状态与 useEffect 执行删除操作

1. 修改工具栏组件,添加确认删除触发逻辑

更新DataTableRowSelectionToolbar组件,新增回调函数传递用户确认删除的操作,同时调整对话框的触发逻辑:

import { useState } from "react";
import { Ingrediant } from "@/types";
import { Table } from "@tanstack/react-table";
import { Button } from "@/Components/ui/button";
import { Trash2 } from "lucide-react";
import { Alert, AlertDescription } from "@/Components/ui/alert";
import {
    AlertDialog,
    AlertDialogAction,
    AlertDialogCancel,
    AlertDialogContent,
    AlertDialogDescription,
    AlertDialogFooter,
    AlertDialogHeader,
    AlertDialogTitle,
    AlertDialogTrigger,
} from "@/Components/ui/alert-dialog";

export default function DataTableRowSelectionToolbar({
    table,
    onConfirmDelete,
}: {
    table: Table<Ingrediant>;
    onConfirmDelete: () => void;
}) {
    const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
    const selectedRowsCount = table.getSelectedRowModel().rows.length;
    const totalRowsCount = table.getFilteredRowModel().rows.length;

    return (
        <div className="space-y-2 pt-4 w-full">
            <div className="flex items-center justify-between">
                <div className="text-sm text-muted-foreground">
                    {selectedRowsCount > 0 ? (
                        <>
                            <span className="font-medium text-foreground">
                                {selectedRowsCount}
                            </span>{" "}
                            of{" "}
                            <span className="font-medium text-foreground">
                                {totalRowsCount}
                            </span>{" "}
                            row(s) selected.
                        </>
                    ) : (
                        <>
                            <span className="font-medium text-foreground">
                                {totalRowsCount}
                            </span>{" "}
                            rows total.
                        </>
                    )}
                </div>
                {selectedRowsCount > 0 && (
                    <Button
                        variant="ghost"
                        size="sm"
                        onClick={() => table.resetRowSelection()}
                    >
                        Clear selection
                    </Button>
                )}
            </div>

            {selectedRowsCount > 0 && (
                <AlertDialog
                    open={isDeleteDialogOpen}
                    onOpenChange={setIsDeleteDialogOpen}
                >
                    <Alert variant="destructive" className="w-full ">
                        <AlertDialogTrigger asChild>
                            <Button
                                variant="destructive"
                                size="icon"
                                onClick={() => setIsDeleteDialogOpen(true)}
                            >
                                <Trash2 className="h-4 w-4 cursor-pointer" />
                            </Button>
                        </AlertDialogTrigger>
                        <AlertDescription className="inline-flex ml-3">
                            Delete selected rows
                        </AlertDescription>
                    </Alert>
                    <AlertDialogContent>
                        <AlertDialogHeader>
                            <AlertDialogTitle>
                                Are you absolutely sure?
                            </AlertDialogTitle>
                            <AlertDialogDescription>
                                This action cannot be undone. This will
                                permanently delete the {selectedRowsCount}{" "}
                                selected row(s) and remove their data from our
                                servers.
                            </AlertDialogDescription>
                        </AlertDialogHeader>
                        <AlertDialogFooter>
                            <AlertDialogCancel onClick={() => setIsDeleteDialogOpen(false)}>Cancel</AlertDialogCancel>
                            <AlertDialogAction
                                onClick={() => {
                                    setIsDeleteDialogOpen(false);
                                    onConfirmDelete();
                                }}
                            >
                                Delete
                            </AlertDialogAction>
                        </AlertDialogFooter>
                    </AlertDialogContent>
                </AlertDialog>
            )}
        </div>
    );
}

2. 父组件中实现状态监听与删除请求

在包含表格的父组件里,新增确认删除状态,通过useEffect监听状态变化,执行批量删除请求并刷新表格:

import { useState, useEffect, useRef } from "react";
import { useRouter } from "@inertiajs/react";
import { query } from "@/utils/query-builder";
import DataTableRowSelectionToolbar from "./DataTableRowSelectionToolbar";
import { useReactTable, getCoreRowModel, getFilteredRowModel, getPaginationRowModel } from "@tanstack/react-table";

export default function IngredientsTable() {
    const router = useRouter();
    const isRendered = useRef(false);
    const [sorting, setSorting] = useState([]);
    const [columnFilters, setColumnFilters] = useState([]);
    const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 });
    const [shouldDelete, setShouldDelete] = useState(false);

    const [table] = useReactTable({
        // 你的表格数据源、列配置等
        getCoreRowModel: getCoreRowModel(),
        getFilteredRowModel: getFilteredRowModel(),
        getPaginationRowModel: getPaginationRowModel(),
        onSortingChange: setSorting,
        onColumnFiltersChange: setColumnFilters,
        onPaginationChange: setPagination,
    });

    // 监听删除确认状态,执行删除操作
    useEffect(() => {
        if (!shouldDelete) return;

        const selectedIds = table.getSelectedRowModel().rows.map(row => row.original.id);
        
        router.post(route("ingredients.bulk-delete"), { ids: selectedIds }, {
            onSuccess: () => {
                setShouldDelete(false);
                table.resetRowSelection();
                // 触发表格数据刷新
                setPagination(prev => ({ ...prev, pageIndex: 0 }));
            },
            onError: (errors) => {
                console.error("删除失败:", errors);
                setShouldDelete(false);
            }
        });
    }, [shouldDelete, table, router]);

    // 原有的分页/排序/筛选逻辑
    useEffect(() => {
        const queryBuilder = query(route(route().current()!));
        if (isRendered.current) {
            const sortQuery = sorting.map(
                (sort) => (sort.desc ? "-" : "") + sort.id
            );
            if (sortQuery.length > 0) queryBuilder.sort(...sortQuery);
            columnFilters.forEach((filter) =>
                queryBuilder.filter(filter.id, filter.value as any)
            );

            const page = pagination.pageIndex + 1;
            queryBuilder
                .page(page)
                .param("per_page", pagination.pageSize.toString());
            if (queryBuilder.build())
                router.get(queryBuilder.build(), undefined, {
                    preserveState: true,
                    preserveScroll: true,
                    replace: true,
                });
        }
        isRendered.current = true;
    }, [sorting, columnFilters, pagination]);

    return (
        <div>
            <DataTableRowSelectionToolbar 
                table={table} 
                onConfirmDelete={() => setShouldDelete(true)}
            />
            {/* 表格内容渲染 */}
        </div>
    );
}

3. Laravel后端处理批量删除

路由配置

Route::post('/ingredients/bulk-delete', [IngredientController::class, 'bulkDelete'])->name('ingredients.bulk-delete');

控制器方法

use Illuminate\Http\Request;
use Spatie\QueryBuilder\QueryBuilder;
use App\Models\Ingredient;

public function bulkDelete(Request $request)
{
    $request->validate([
        'ids' => 'required|array|exists:ingredients,id',
    ]);

    QueryBuilder::for(Ingredient::class)
        ->whereIn('id', $request->ids)
        ->delete();

    return redirect()->back()->with('success', '选中记录已删除');
}

内容的提问来源于stack exchange,提问作者EL MAHDI BOUZKOURA

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 06:55:04