Skip to content

Commit daca68b

Browse files
authored
Merge pull request #10876 from marmelab/use-delete-controller
Introduce `useDeleteController`
2 parents 31204a0 + ca228a4 commit daca68b

File tree

7 files changed

+396
-273
lines changed

7 files changed

+396
-273
lines changed

packages/ra-core/src/controller/button/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ import useDeleteWithUndoController from './useDeleteWithUndoController';
22
import useDeleteWithConfirmController from './useDeleteWithConfirmController';
33

44
export { useDeleteWithUndoController, useDeleteWithConfirmController };
5+
export * from './useDeleteController';
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import { useCallback, useMemo } from 'react';
2+
import { UseMutationOptions } from '@tanstack/react-query';
3+
4+
import { useDelete } from '../../dataProvider';
5+
import { useUnselect } from '../';
6+
import { useRedirect, RedirectionSideEffect } from '../../routing';
7+
import { useNotify } from '../../notification';
8+
import { RaRecord, MutationMode, DeleteParams } from '../../types';
9+
import { useResourceContext } from '../../core';
10+
import { useTranslate } from '../../i18n';
11+
12+
/**
13+
* Prepare a set of callbacks for a delete button
14+
*
15+
* @example
16+
* const DeleteButton = ({
17+
* resource,
18+
* record,
19+
* redirect,
20+
* ...rest
21+
* }) => {
22+
* const {
23+
* isPending,
24+
* handleDelete,
25+
* } = useDeleteController({
26+
* mutationMode: 'pessimistic',
27+
* resource,
28+
* record,
29+
* redirect,
30+
* });
31+
*
32+
* const [open, setOpen] = useState(false);
33+
*
34+
* return (
35+
* <Fragment>
36+
* <Button
37+
* onClick={() => setOpen(true)}
38+
* label="ra.action.delete"
39+
* {...rest}
40+
* >
41+
* {icon}
42+
* </Button>
43+
* <Confirm
44+
* isOpen={open}
45+
* loading={isPending}
46+
* title="ra.message.delete_title"
47+
* content="ra.message.delete_content"
48+
* titleTranslateOptions={{
49+
* name: resource,
50+
* id: record.id,
51+
* }}
52+
* contentTranslateOptions={{
53+
* name: resource,
54+
* id: record.id,
55+
* }}
56+
* onConfirm={() => handleDelete()}
57+
* onClose={() => setOpen(false)}
58+
* />
59+
* </Fragment>
60+
* );
61+
* };
62+
*/
63+
export const useDeleteController = <
64+
RecordType extends RaRecord = any,
65+
ErrorType = Error,
66+
>(
67+
props: UseDeleteControllerParams<RecordType, ErrorType>
68+
): UseDeleteControllerReturn => {
69+
const {
70+
record,
71+
redirect: redirectTo = 'list',
72+
mutationMode,
73+
mutationOptions = {},
74+
successMessage,
75+
} = props;
76+
const { meta: mutationMeta, ...otherMutationOptions } = mutationOptions;
77+
const resource = useResourceContext(props);
78+
const notify = useNotify();
79+
const unselect = useUnselect(resource);
80+
const redirect = useRedirect();
81+
const translate = useTranslate();
82+
83+
const [deleteOne, { isPending }] = useDelete<RecordType, ErrorType>(
84+
resource,
85+
undefined,
86+
{
87+
onSuccess: () => {
88+
notify(
89+
successMessage ??
90+
`resources.${resource}.notifications.deleted`,
91+
{
92+
type: 'info',
93+
messageArgs: {
94+
smart_count: 1,
95+
_: translate('ra.notification.deleted', {
96+
smart_count: 1,
97+
}),
98+
},
99+
undoable: mutationMode === 'undoable',
100+
}
101+
);
102+
record && unselect([record.id]);
103+
redirect(redirectTo, resource);
104+
},
105+
onError: error => {
106+
notify(
107+
typeof error === 'string'
108+
? error
109+
: (error as Error)?.message ||
110+
'ra.notification.http_error',
111+
{
112+
type: 'error',
113+
messageArgs: {
114+
_:
115+
typeof error === 'string'
116+
? error
117+
: (error as Error)?.message
118+
? (error as Error).message
119+
: undefined,
120+
},
121+
}
122+
);
123+
},
124+
}
125+
);
126+
127+
const handleDelete = useCallback(() => {
128+
if (!record) {
129+
throw new Error(
130+
'The record cannot be deleted because no record has been passed'
131+
);
132+
}
133+
deleteOne(
134+
resource,
135+
{
136+
id: record.id,
137+
previousData: record,
138+
meta: mutationMeta,
139+
},
140+
{
141+
mutationMode,
142+
...otherMutationOptions,
143+
}
144+
);
145+
}, [
146+
deleteOne,
147+
mutationMeta,
148+
mutationMode,
149+
otherMutationOptions,
150+
record,
151+
resource,
152+
]);
153+
154+
return useMemo(
155+
() => ({
156+
isPending,
157+
isLoading: isPending,
158+
handleDelete,
159+
}),
160+
[isPending, handleDelete]
161+
);
162+
};
163+
164+
export interface UseDeleteControllerParams<
165+
RecordType extends RaRecord = any,
166+
MutationOptionsError = unknown,
167+
> {
168+
mutationMode?: MutationMode;
169+
mutationOptions?: UseMutationOptions<
170+
RecordType,
171+
MutationOptionsError,
172+
DeleteParams<RecordType>
173+
>;
174+
record?: RecordType;
175+
redirect?: RedirectionSideEffect;
176+
resource?: string;
177+
successMessage?: string;
178+
}
179+
180+
export interface UseDeleteControllerReturn {
181+
isLoading: boolean;
182+
isPending: boolean;
183+
handleDelete: () => void;
184+
}

0 commit comments

Comments
 (0)