import { useCallback, useMemo } from 'react';
import { useFormContext } from 'react-hook-form';
import { useDelete, UseDeleteOptions } from '../../dataProvider/useDelete';
import { useUnselect } from '../list/useUnselect';
import { useRecordContext } from '../record/useRecordContext';
import { useRedirect, RedirectionSideEffect } from '../../routing/useRedirect';
import { useNotify } from '../../notification/useNotify';
import { RaRecord, MutationMode } from '../../types';
import { useResourceContext } from '../../core/useResourceContext';
import { useTranslate } from '../../i18n/useTranslate';
/**
* Prepare a set of callbacks for a delete button
*
* @example
* const DeleteButton = ({
* redirect,
* ...rest
* }) => {
* const {
* isPending,
* handleDelete,
* } = useDeleteController({
* mutationMode: 'pessimistic',
* redirect,
* });
*
* const [open, setOpen] = useState(false);
*
* return (
*
*
* handleDelete()}
* onClose={() => setOpen(false)}
* />
*
* );
* };
*/
export const useDeleteController = <
RecordType extends RaRecord = any,
ErrorType = Error,
>(
props: UseDeleteControllerParams
): UseDeleteControllerReturn => {
const {
redirect: redirectTo = 'list',
mutationMode = 'undoable',
mutationOptions = {},
successMessage,
} = props;
const { meta: mutationMeta, ...otherMutationOptions } = mutationOptions;
const record = useRecordContext(props);
const resource = useResourceContext(props);
const notify = useNotify();
const unselect = useUnselect(resource);
const redirect = useRedirect();
const translate = useTranslate();
// null when the delete button is used outside a form (e.g. in a list)
const form = useFormContext();
const [deleteOne, { isPending }] = useDelete(
resource,
undefined,
{
onSuccess: () => {
notify(
successMessage ??
`resources.${resource}.notifications.deleted`,
{
type: 'info',
messageArgs: {
smart_count: 1,
_: translate('ra.notification.deleted', {
smart_count: 1,
}),
},
undoable: mutationMode === 'undoable',
}
);
record && unselect([record.id], true);
// Clear the form dirty state so warnWhenUnsavedChanges doesn't
// block the redirect: the record is gone, so the unsaved
// changes are moot. keepValues avoids a flash of reverted
// fields before the redirect commits.
form?.reset(undefined, { keepValues: true });
redirect(redirectTo, resource);
},
onError: (error: any) => {
notify(
typeof error === 'string'
? error
: error?.message || 'ra.notification.http_error',
{
type: 'error',
messageArgs: {
_:
typeof error === 'string'
? error
: error?.message,
},
}
);
},
}
);
const handleDelete = useCallback(() => {
if (!record) {
throw new Error(
'The record cannot be deleted because no record has been passed'
);
}
deleteOne(
resource,
{
id: record.id,
previousData: record,
meta: mutationMeta,
},
{
mutationMode,
...otherMutationOptions,
}
);
}, [
deleteOne,
mutationMeta,
mutationMode,
otherMutationOptions,
record,
resource,
]);
return useMemo(
() => ({
isPending,
isLoading: isPending,
handleDelete,
}),
[isPending, handleDelete]
);
};
export interface UseDeleteControllerParams<
RecordType extends RaRecord = any,
MutationOptionsError = unknown,
> {
mutationMode?: MutationMode;
mutationOptions?: UseDeleteOptions;
record?: RecordType;
redirect?: RedirectionSideEffect;
resource?: string;
successMessage?: string;
}
export interface UseDeleteControllerReturn {
isLoading: boolean;
isPending: boolean;
handleDelete: () => void;
}