2.0 beta — new releases can include breaking changes. Stable 1.x docs
dimah-s3v2.0.0-beta.0

useDelete

Headless React hook that deletes one or many objects, with state for each key.

useDelete deletes objects through your server. Each key has its own state, so a list can show which rows are being deleted. A second call for a key that is already being deleted joins that call.

components/delete-file.tsx
"use client";

import { useDelete } from "@dimah-s3/react";

export function DeleteFile({ objectKey }: { objectKey: string }) {
  const { remove, get } = useDelete({ route: "attachments" });
  const operation = get(objectKey);

  return (
    <button
      type="button"
      onClick={() => void remove(objectKey)}
      disabled={operation?.phase === "deleting"}
    >
      {operation?.phase === "deleting" ? "Deleting…" : "Delete"}
    </button>
  );
}

The hook has no confirm state. To ask first, use the ui Delete root, which owns one confirm dialog for every trigger inside it, or open your own dialog and call remove when the user confirms.


Delete one key

remove(key, { fileName }) resolves with an outcome and does not throw. fileName is stored on the operation for display, such as in a toast.

const outcome = await remove(objectKey, { fileName: "invoice.pdf" });

if (outcome.status === "success") console.log("Deleted", outcome.data.key);
if (outcome.status === "error") console.log(outcome.error.code);
if (outcome.status === "blocked") console.log("beforeDelete said no");

A delete has no cancel, so it never ends aborted.

Delete many keys

removeMany(items) takes any number of keys. The hook sends them in batches of up to 100 (DELETE_MANY_MAX_KEYS), one request after another, and every operation of the call shares one batchId. The server deletes each key on its own, so some can fail while others succeed:

const { deleted, failed, blocked } = await removeMany([
  "attachments/a.pdf",
  { key: "attachments/b.pdf", fileName: "b.pdf" },
]);

for (const { key, error } of failed) console.log(key, error.code);

Keys that are already being deleted are joined rather than sent again, and their outcomes are folded into the result.


State

  • operations — every delete still kept, in the order it started. Each has key, fileName, batchId, phase (deleting, success, or error), and error.
  • get(key) — the newest operation for a key.
  • pendingKeys — keys with a running delete.
  • isPending — true while any key is being deleted.
  • reset(key?) — removes settled operations for one key, or every settled operation. It does not cancel a running delete.

Callbacks

CallbackRuns
beforeDelete(keys)Once per call. Return false to block the keys. A throw fails them (HOOK_FAILED).
onDeleteStart(keys)Once per call, before the request.
onError(error, key)Once per failed key.
onSuccess(data, key)Once per deleted key, after every onError of the call.
onSettled(result)Once per call, last.

data is { key, bucket? }. bucket is set by remove only, because batch responses do not carry it. A call that joins a running delete does not run the callbacks again.


Type reference

import type {
  DeleteOperation,
  DeleteResult,
  UseDeleteOptions,
  UseDeleteReturn,
} from "@dimah-s3/react";

UseDeleteOptions

Prop

Type

UseDeleteReturn

Prop

Type

DeleteOperation

Prop

Type

DeleteResult

Prop

Type

Frequently asked questions

On this page