Skip to content

Commit

Permalink
Merge pull request #10445 from marmelab/allow-passing-error-type
Browse files Browse the repository at this point in the history
[TypeScript] Allow providing error type in dataProvider and controllers hooks
  • Loading branch information
slax57 authored Jan 15, 2025
2 parents b605d16 + 2bb4e3f commit 6b1367d
Show file tree
Hide file tree
Showing 28 changed files with 383 additions and 236 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,11 @@ import { useTranslate } from '../../i18n';
* );
* };
*/
const useDeleteWithConfirmController = <RecordType extends RaRecord = any>(
props: UseDeleteWithConfirmControllerParams<RecordType>
const useDeleteWithConfirmController = <
RecordType extends RaRecord = any,
ErrorType = Error,
>(
props: UseDeleteWithConfirmControllerParams<RecordType, ErrorType>
): UseDeleteWithConfirmControllerReturn => {
const {
record,
Expand All @@ -83,7 +86,7 @@ const useDeleteWithConfirmController = <RecordType extends RaRecord = any>(
const redirect = useRedirect();
const translate = useTranslate();

const [deleteOne, { isPending }] = useDelete<RecordType>(
const [deleteOne, { isPending }] = useDelete<RecordType, ErrorType>(
resource,
undefined,
{
Expand All @@ -106,21 +109,22 @@ const useDeleteWithConfirmController = <RecordType extends RaRecord = any>(
record && unselect([record.id]);
redirect(redirectTo, resource);
},
onError: (error: Error) => {
onError: error => {
setOpen(false);

notify(
typeof error === 'string'
? error
: error.message || 'ra.notification.http_error',
: (error as Error)?.message ||
'ra.notification.http_error',
{
type: 'error',
messageArgs: {
_:
typeof error === 'string'
? error
: error && error.message
? error.message
: (error as Error)?.message
? (error as Error).message
: undefined,
},
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,11 @@ import { useTranslate } from '../../i18n';
* );
* };
*/
const useDeleteWithUndoController = <RecordType extends RaRecord = any>(
props: UseDeleteWithUndoControllerParams<RecordType>
const useDeleteWithUndoController = <
RecordType extends RaRecord = any,
ErrorType = Error,
>(
props: UseDeleteWithUndoControllerParams<RecordType, ErrorType>
): UseDeleteWithUndoControllerReturn => {
const {
record,
Expand All @@ -60,7 +63,7 @@ const useDeleteWithUndoController = <RecordType extends RaRecord = any>(
const unselect = useUnselect(resource);
const redirect = useRedirect();
const translate = useTranslate();
const [deleteOne, { isPending }] = useDelete<RecordType>(
const [deleteOne, { isPending }] = useDelete<RecordType, ErrorType>(
resource,
undefined,
{
Expand All @@ -82,19 +85,20 @@ const useDeleteWithUndoController = <RecordType extends RaRecord = any>(
record && unselect([record.id]);
redirect(redirectTo, resource);
},
onError: (error: Error) => {
onError: error => {
notify(
typeof error === 'string'
? error
: error.message || 'ra.notification.http_error',
: (error as Error)?.message ||
'ra.notification.http_error',
{
type: 'error',
messageArgs: {
_:
typeof error === 'string'
? error
: error && error.message
? error.message
: (error as Error)?.message
? (error as Error).message
: undefined,
},
}
Expand Down
15 changes: 11 additions & 4 deletions packages/ra-core/src/controller/create/CreateController.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
CreateControllerProps,
CreateControllerResult,
} from './useCreateController';
import { RaRecord } from '../../types';

/**
* Render prop version of the useCreateController hook
Expand All @@ -18,12 +19,18 @@ import {
* </CreateController>
* );
*/
export const CreateController = ({
export const CreateController = <
RecordType extends Omit<RaRecord, 'id'> = any,
MutationOptionsError = Error,
>({
children,
...props
}: {
children: (params: CreateControllerResult) => ReactNode;
} & CreateControllerProps) => {
const controllerProps = useCreateController(props);
children: (params: CreateControllerResult<RecordType>) => ReactNode;
} & CreateControllerProps<RecordType, MutationOptionsError>) => {
const controllerProps = useCreateController<
RecordType,
MutationOptionsError
>(props);
return children(controllerProps);
};
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const EditContextProvider = ({
value,
}: {
children: ReactNode;
value: EditControllerResult;
value: EditControllerResult<any, any>;
}) => (
<EditContext.Provider value={value}>
<SaveContextProvider value={usePickSaveContext(value)}>
Expand Down
14 changes: 10 additions & 4 deletions packages/ra-core/src/controller/edit/EditController.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
EditControllerProps,
EditControllerResult,
} from './useEditController';
import { RaRecord } from '../../types';

/**
* Render prop version of the useEditController hook
Expand All @@ -18,12 +19,17 @@ import {
* </EditController>
* );
*/
export const EditController = ({
export const EditController = <
RecordType extends RaRecord = any,
ErrorType = Error,
>({
children,
...props
}: {
children: (params: EditControllerResult) => ReactNode;
} & EditControllerProps) => {
const controllerProps = useEditController(props);
children: (
params: EditControllerResult<RecordType, ErrorType>
) => ReactNode;
} & EditControllerProps<RecordType, ErrorType>) => {
const controllerProps = useEditController<RecordType, ErrorType>(props);
return children(controllerProps);
};
5 changes: 3 additions & 2 deletions packages/ra-core/src/controller/edit/useEditContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ import { EditControllerResult } from './useEditController';
*/
export const useEditContext = <
RecordType extends RaRecord = any,
>(): EditControllerResult<RecordType> => {
ErrorType = Error,
>(): EditControllerResult<RecordType, ErrorType> => {
const context = useContext(EditContext);
if (!context) {
throw new Error(
'useEditContext must be used inside an EditContextProvider'
);
}
return context;
return context as EditControllerResult<RecordType, ErrorType>;
};
17 changes: 10 additions & 7 deletions packages/ra-core/src/controller/edit/useEditController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const useEditController = <
ErrorType = Error,
>(
props: EditControllerProps<RecordType, ErrorType> = {}
): EditControllerResult<RecordType> => {
): EditControllerResult<RecordType, ErrorType> => {
const {
disableAuthentication = false,
id: propsId,
Expand Down Expand Up @@ -111,7 +111,7 @@ export const useEditController = <
isFetching,
isPending,
refetch,
} = useGetOne<RecordType>(
} = useGetOne<RecordType, ErrorType>(
resource,
{ id, meta: queryMeta },
{
Expand Down Expand Up @@ -279,7 +279,7 @@ export const useEditController = <
save,
saving,
unregisterMutationMiddleware,
} as EditControllerResult<RecordType>;
} as EditControllerResult<RecordType, ErrorType>;
};

const DefaultRedirect = 'list';
Expand All @@ -292,7 +292,7 @@ export interface EditControllerProps<
id?: RecordType['id'];
mutationMode?: MutationMode;
mutationOptions?: UseUpdateOptions<RecordType, ErrorType>;
queryOptions?: UseGetOneOptions<RecordType>;
queryOptions?: UseGetOneOptions<RecordType, ErrorType>;
redirect?: RedirectionSideEffect;
resource?: string;
transform?: TransformData;
Expand Down Expand Up @@ -339,8 +339,11 @@ export interface EditControllerSuccessResult<RecordType extends RaRecord = any>
isPending: false;
}

export type EditControllerResult<RecordType extends RaRecord = any> =
export type EditControllerResult<
RecordType extends RaRecord = any,
ErrorType = Error,
> =
| EditControllerLoadingResult<RecordType>
| EditControllerLoadingErrorResult<RecordType>
| EditControllerRefetchErrorResult<RecordType>
| EditControllerLoadingErrorResult<RecordType, ErrorType>
| EditControllerRefetchErrorResult<RecordType, ErrorType>
| EditControllerSuccessResult<RecordType>;
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { UseQueryOptions } from '@tanstack/react-query';
export interface UseReferenceArrayFieldControllerParams<
RecordType extends RaRecord = RaRecord,
ReferenceRecordType extends RaRecord = RaRecord,
ErrorType = Error,
> {
filter?: any;
page?: number;
Expand All @@ -18,7 +19,7 @@ export interface UseReferenceArrayFieldControllerParams<
sort?: SortPayload;
source: string;
queryOptions?: Omit<
UseQueryOptions<ReferenceRecordType[]>,
UseQueryOptions<ReferenceRecordType[], ErrorType>,
'queryFn' | 'queryKey'
>;
}
Expand Down Expand Up @@ -52,12 +53,14 @@ const defaultFilter = {};
export const useReferenceArrayFieldController = <
RecordType extends RaRecord = RaRecord,
ReferenceRecordType extends RaRecord = RaRecord,
ErrorType = Error,
>(
props: UseReferenceArrayFieldControllerParams<
RecordType,
ReferenceRecordType
ReferenceRecordType,
ErrorType
>
): ListControllerResult => {
): ListControllerResult<ReferenceRecordType, ErrorType> => {
const {
filter = defaultFilter,
page = 1,
Expand All @@ -74,23 +77,24 @@ export const useReferenceArrayFieldController = <
const ids = Array.isArray(value) ? value : emptyArray;

const { data, error, isLoading, isFetching, isPending, refetch } =
useGetManyAggregate<ReferenceRecordType>(
useGetManyAggregate<ReferenceRecordType, ErrorType>(
reference,
{ ids, meta },
{
onError: error =>
notify(
typeof error === 'string'
? error
: error.message || 'ra.notification.http_error',
: (error as Error)?.message ||
'ra.notification.http_error',
{
type: 'error',
messageArgs: {
_:
typeof error === 'string'
? error
: error && error.message
? error.message
: (error as Error)?.message
? (error as Error).message
: undefined,
},
}
Expand All @@ -99,7 +103,7 @@ export const useReferenceArrayFieldController = <
}
);

const listProps = useList<ReferenceRecordType>({
const listProps = useList<ReferenceRecordType, ErrorType>({
data,
error,
filter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,18 @@ import { useFieldValue } from '../../util';

export const useReferenceFieldController = <
ReferenceRecordType extends RaRecord = RaRecord,
ErrorType = Error,
>(
options: UseReferenceFieldControllerOptions<ReferenceRecordType>
): UseReferenceFieldControllerResult<ReferenceRecordType> => {
options: UseReferenceFieldControllerOptions<ReferenceRecordType, ErrorType>
): UseReferenceFieldControllerResult<ReferenceRecordType, ErrorType> => {
const { link, reference, queryOptions } = options;
if (!reference) {
throw new Error(
'useReferenceFieldController: missing reference prop. You must provide a reference, e.g. reference="posts".'
);
}
const id = useFieldValue(options);
const referenceRecordQuery = useReference<ReferenceRecordType>({
const referenceRecordQuery = useReference<ReferenceRecordType, ErrorType>({
reference,
id,
options: {
Expand Down Expand Up @@ -50,10 +51,11 @@ export const useReferenceFieldController = <

export interface UseReferenceFieldControllerOptions<
ReferenceRecordType extends RaRecord = RaRecord,
ErrorType = Error,
> {
source: string;
queryOptions?: Omit<
UseQueryOptions<ReferenceRecordType[], Error>,
UseQueryOptions<ReferenceRecordType[], ErrorType>,
'queryFn' | 'queryKey'
>;
reference: string;
Expand All @@ -62,6 +64,7 @@ export interface UseReferenceFieldControllerOptions<

export interface UseReferenceFieldControllerResult<
ReferenceRecordType extends RaRecord = RaRecord,
> extends UseReferenceResult<ReferenceRecordType> {
ErrorType = Error,
> extends UseReferenceResult<ReferenceRecordType, ErrorType> {
link?: string | false;
}
Loading

0 comments on commit 6b1367d

Please sign in to comment.