Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TypeScript] Allow providing error type in dataProvider and controllers hooks #10445

Merged
merged 4 commits into from
Jan 15, 2025
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Allow providing error type in controllers hooks and components
djhi committed Jan 14, 2025
commit 45d824ecd65b17df9045174c1c7126dcdfbc798e
Original file line number Diff line number Diff line change
@@ -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,
@@ -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,
{
@@ -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,
},
}
Original file line number Diff line number Diff line change
@@ -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,
@@ -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,
{
@@ -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,
},
}
15 changes: 11 additions & 4 deletions packages/ra-core/src/controller/create/CreateController.tsx
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import {
CreateControllerProps,
CreateControllerResult,
} from './useCreateController';
import { RaRecord } from '../../types';

/**
* Render prop version of the useCreateController hook
@@ -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
@@ -34,7 +34,7 @@ export const EditContextProvider = ({
value,
}: {
children: ReactNode;
value: EditControllerResult;
value: EditControllerResult<any, any>;
}) => (
<EditContext.Provider value={value}>
<SaveContextProvider value={usePickSaveContext(value)}>
14 changes: 10 additions & 4 deletions packages/ra-core/src/controller/edit/EditController.tsx
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import {
EditControllerProps,
EditControllerResult,
} from './useEditController';
import { RaRecord } from '../../types';

/**
* Render prop version of the useEditController hook
@@ -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
@@ -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
@@ -53,7 +53,7 @@ export const useEditController = <
ErrorType = Error,
>(
props: EditControllerProps<RecordType, ErrorType> = {}
): EditControllerResult<RecordType> => {
): EditControllerResult<RecordType, ErrorType> => {
const {
disableAuthentication = false,
id: propsId,
@@ -111,7 +111,7 @@ export const useEditController = <
isFetching,
isPending,
refetch,
} = useGetOne<RecordType>(
} = useGetOne<RecordType, ErrorType>(
resource,
{ id, meta: queryMeta },
{
@@ -279,7 +279,7 @@ export const useEditController = <
save,
saving,
unregisterMutationMiddleware,
} as EditControllerResult<RecordType>;
} as EditControllerResult<RecordType, ErrorType>;
};

const DefaultRedirect = 'list';
@@ -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;
@@ -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
@@ -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;
@@ -18,7 +19,7 @@ export interface UseReferenceArrayFieldControllerParams<
sort?: SortPayload;
source: string;
queryOptions?: Omit<
UseQueryOptions<ReferenceRecordType[]>,
UseQueryOptions<ReferenceRecordType[], ErrorType>,
'queryFn' | 'queryKey'
>;
}
@@ -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,
@@ -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,
},
}
@@ -99,7 +103,7 @@ export const useReferenceArrayFieldController = <
}
);

const listProps = useList<ReferenceRecordType>({
const listProps = useList<ReferenceRecordType, ErrorType>({
data,
error,
filter,
Original file line number Diff line number Diff line change
@@ -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: {
@@ -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;
@@ -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