Skip to main content
useDeleteMany is a modified version of TanStack Query’s useMutation for multiple delete mutations. It uses the deleteMany method from the dataProvider. If the dataProvider doesn’t have a deleteMany method, it will call deleteOne for each item individually.

Usage

import { useDeleteMany } from "@refinedev/core";

const { mutate } = useDeleteMany();

mutate({
  resource: "products",
  ids: [1, 2, 3],
});

Parameters

resource
string
required
Resource name for API data interactions.
ids
BaseKey[]
required
Array of record IDs to delete.
mutationMode
'pessimistic' | 'optimistic' | 'undoable'
Determines when mutations are executed. See mutation modes.
undoableTimeout
number
Duration in ms to wait before executing the mutation when mutationMode = "undoable".
onCancel
(cancelMutation: () => void) => void
Provides a function to cancel the mutation when mutationMode = "undoable".
meta
MetaQuery
Metadata for the dataProvider. Can be used to pass additional parameters to data provider methods.
dataProviderName
string
If there is more than one dataProvider, you should specify which one to use.
invalidates
Array<keyof IQueryKeys>
default:"['list', 'many']"
Specify which queries should be invalidated after successful mutation. Options: 'list', 'many', 'detail', 'resourceAll', 'all'.
values
TVariables
Optional values to pass to the delete method (some APIs may require additional data for deletion).
successNotification
OpenNotificationParams | false | ((data, ids) => OpenNotificationParams | false)
Success notification configuration. Set to false to disable.
errorNotification
OpenNotificationParams | false | ((error, ids) => OpenNotificationParams | false)
Error notification configuration. Set to false to disable.
mutationOptions
UseMutationOptions
TanStack Query’s useMutation options.
overtimeOptions
UseLoadingOvertimeOptionsProps
Configuration for loading overtime behavior.

Return Values

mutate
(params: DeleteManyParams) => void
Function to trigger the mutation.
mutateAsync
(params: DeleteManyParams) => Promise<DeleteManyResponse<TData>>
Async version that returns a promise.
mutation
UseMutationResult
TanStack Query’s useMutation return object.
overtime
object
Loading overtime information.

Examples

Basic Usage

import { useDeleteMany } from "@refinedev/core";

const { mutate } = useDeleteMany();

const handleDelete = (ids: number[]) => {
  mutate({
    resource: "products",
    ids,
  });
};

Bulk Delete with Confirmation

import { useDeleteMany } from "@refinedev/core";

const BulkDelete = ({ selectedIds }: { selectedIds: number[] }) => {
  const { mutate, mutation } = useDeleteMany();

  const handleDelete = () => {
    if (window.confirm(`Delete ${selectedIds.length} items?`)) {
      mutate({
        resource: "products",
        ids: selectedIds,
      });
    }
  };

  return (
    <button onClick={handleDelete} disabled={mutation.isPending}>
      {mutation.isPending ? "Deleting..." : `Delete ${selectedIds.length} items`}
    </button>
  );
};

With Optimistic Updates

const { mutate } = useDeleteMany();

mutate({
  resource: "products",
  ids: [1, 2, 3],
  mutationMode: "optimistic",
});

With Undoable Mode

const { mutate } = useDeleteMany();

mutate({
  resource: "products",
  ids: [1, 2, 3],
  mutationMode: "undoable",
  undoableTimeout: 5000, // 5 seconds to undo
  onCancel: (cancelMutation) => {
    // Provide cancel button in your UI
    document.getElementById("undoBtn")?.addEventListener("click", cancelMutation);
  },
});

With Async/Await

const { mutateAsync } = useDeleteMany();

const handleDelete = async (ids: number[]) => {
  try {
    const result = await mutateAsync({
      resource: "products",
      ids,
    });
    console.log("Deleted products:", result.data);
  } catch (error) {
    console.error("Failed to delete products:", error);
  }
};

With Callbacks

const { mutate } = useDeleteMany({
  mutationOptions: {
    onSuccess: (data) => {
      console.log("Deleted successfully");
    },
    onError: (error) => {
      console.error("Error:", error.message);
    },
  },
});

mutate({
  resource: "products",
  ids: [1, 2, 3],
});

Disable Notifications

const { mutate } = useDeleteMany();

mutate({
  resource: "products",
  ids: [1, 2, 3],
  successNotification: false,
  errorNotification: false,
});

With Meta Data

const { mutate } = useDeleteMany();

mutate({
  resource: "products",
  ids: [1, 2, 3],
  meta: {
    headers: { "X-Custom-Header": "value" },
  },
});

Soft Delete

const { mutate } = useDeleteMany();

mutate({
  resource: "products",
  ids: [1, 2, 3],
  values: {
    deletedAt: new Date().toISOString(),
  },
});

With Loading State

const { mutate, mutation } = useDeleteMany();

const handleDelete = (ids: number[]) => {
  mutate({
    resource: "products",
    ids,
  });
};

return (
  <div>
    <button onClick={() => handleDelete([1, 2, 3])} disabled={mutation.isPending}>
      {mutation.isPending ? "Deleting..." : "Delete Selected"}
    </button>
    {mutation.isError && <p>Error: {mutation.error.message}</p>}
    {mutation.isSuccess && <p>Successfully deleted!</p>}
  </div>
);

Type Parameters

  • TData - Result data type of the mutation. Extends BaseRecord.
  • TError - Custom error type that extends HttpError.
  • TVariables - Type of additional variables/payload for the mutation.

FAQ

If the dataProvider doesn’t implement the deleteMany method, Refine will automatically fall back to calling the deleteOne method for each ID individually.
  • pessimistic (default): UI updates after server confirms
  • optimistic: UI updates immediately, rolls back on error
  • undoable: UI updates immediately with option to undo before server request
Learn more about mutation modes
Yes, set invalidates to an empty array:
mutate({
  resource: "products",
  ids: [1, 2, 3],
  invalidates: [],
});
Pass a values object with your soft delete fields:
mutate({
  resource: "products",
  ids: [1, 2, 3],
  values: {
    deletedAt: new Date().toISOString(),
    isDeleted: true,
  },
});

Build docs developers (and LLMs) love