Skip to content

Commit

Permalink
Merge pull request #6680 from marmelab/return-read-error-in-controllers
Browse files Browse the repository at this point in the history
Returns error from controllers & add support for custom onFailure on useShowController
  • Loading branch information
djhi authored Oct 14, 2021
2 parents f9f745d + 98891ea commit d84013a
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 24 deletions.
3 changes: 2 additions & 1 deletion docs/CreateEdit.md
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ const PostEdit = props => {

The `onFailure` function receives the error from the dataProvider call (`dataProvider.create()` or `dataProvider.update()`), which is a JavaScript Error object (see [the dataProvider documentation for details](./DataProviders.md#error-format)).

The default `onOnFailure` function is:
The default `onFailure` function is:

```jsx
// for the <Create> component:
Expand Down Expand Up @@ -739,6 +739,7 @@ const MyEdit = props => {
const {
basePath, // deduced from the location, useful for action buttons
defaultTitle, // the translated title based on the resource, e.g. 'Post #123'
error, // error returned by dataProvider when it failed to fetch the record. Useful if you want to adapt the view instead of just showing a notification using the `onFailure` side effect.
loaded, // boolean that is false until the record is available
loading, // boolean that is true on mount, and false once the record was fetched
record, // record fetched via dataProvider.getOne() based on the id from the location
Expand Down
46 changes: 46 additions & 0 deletions docs/Show.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Here are all the props accepted by the `<Show>` component:
* [`actions`](#actions)
* [`aside`](#aside-component)
* [`component`](#component)
* [`onFailure`](#onfailure)

### CSS API

Expand Down Expand Up @@ -194,6 +195,50 @@ const PostShow = props => (

The default value for the `component` prop is `Card`.


### `onFailure`

By default, when the getOne action fails at the dataProvider level, react-admin shows an error notification and refreshes the page.

You can override this behavior and pass custom side effects by providing a function as `onFailure` prop:

```jsx
import * as React from 'react';
import { useNotify, useRefresh, useRedirect, Show, SimpleShowLayout } from 'react-admin';

const PostShow = props => {
const notify = useNotify();
const refresh = useRefresh();
const redirect = useRedirect();

const onFailure = (error) => {
notify(`Could not load post: ${error.message}`)
redirect('/posts');
refresh();
};

return (
<Show onFailure={onFailure} {...props}>
<SimpleShowLayout>
...
</SimpleShowLayout>
</Show>
);
}
```

The `onFailure` function receives the error from the dataProvider call (`dataProvider.getOne()`), which is a JavaScript Error object (see [the dataProvider documentation for details](./DataProviders.md#error-format)).

The default `onFailure` function is:

```jsx
(error) => {
notify('ra.notification.item_doesnt_exist', 'warning');
redirect('list', basePath);
refresh();
}
```

## The `<ShowGuesser>` component

Instead of a custom `Show`, you can use the `ShowGuesser` to determine which fields to use based on the data returned by the API.
Expand Down Expand Up @@ -237,6 +282,7 @@ const MyShow = props => {
const {
basePath, // deduced from the location, useful for action buttons
defaultTitle, // the translated title based on the resource, e.g. 'Post #123'
error, // error returned by dataProvider when it failed to fetch the record. Useful if you want to adapt the view instead of just showing a notification using the `onFailure` side effect.
loaded, // boolean that is false until the record is available
loading, // boolean that is true on mount, and false once the record was fetched
record, // record fetched via dataProvider.getOne() based on the id from the location
Expand Down
24 changes: 12 additions & 12 deletions packages/ra-core/src/controller/details/useEditController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export interface EditControllerProps<RecordType extends Record = Record> {
// Necessary for actions (EditActions) which expect a data prop containing the record
// @deprecated - to be removed in 4.0d
data?: RecordType;
error?: any;
defaultTitle: string;
hasCreate?: boolean;
hasEdit?: boolean;
Expand Down Expand Up @@ -138,18 +139,16 @@ export const useEditController = <RecordType extends Record = Record>(
setTransform,
} = useSaveModifiers({ onSuccess, onFailure, transform });

const { data: record, loading, loaded, refetch } = useGetOne<RecordType>(
resource,
id,
{
action: CRUD_GET_ONE,
onFailure: () => {
notify('ra.notification.item_doesnt_exist', 'warning');
redirect('list', basePath);
refresh();
},
}
);
const { data: record, error, loading, loaded, refetch } = useGetOne<
RecordType
>(resource, id, {
action: CRUD_GET_ONE,
onFailure: () => {
notify('ra.notification.item_doesnt_exist', 'warning');
redirect('list', basePath);
refresh();
},
});

const getResourceLabel = useGetResourceLabel();
const defaultTitle = translate('ra.page.edit', {
Expand Down Expand Up @@ -248,6 +247,7 @@ export const useEditController = <RecordType extends Record = Record>(
);

return {
error,
loading,
loaded,
saving,
Expand Down
33 changes: 22 additions & 11 deletions packages/ra-core/src/controller/details/useShowController.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import useVersion from '../useVersion';
import { useCheckMinimumRequiredProps } from '../checkMinimumRequiredProps';
import { Record, Identifier } from '../../types';
import { Record, Identifier, OnFailure } from '../../types';
import { useGetOne, Refetch } from '../../dataProvider';
import { useTranslate } from '../../i18n';
import { useNotify, useRedirect, useRefresh } from '../../sideEffect';
Expand All @@ -14,6 +14,7 @@ export interface ShowProps {
hasShow?: boolean;
hasList?: boolean;
id?: Identifier;
onFailure?: OnFailure;
resource?: string;
[key: string]: any;
}
Expand All @@ -24,6 +25,7 @@ export interface ShowControllerProps<RecordType extends Record = Record> {
// Necessary for actions (EditActions) which expect a data prop containing the record
// @deprecated - to be removed in 4.0d
data?: RecordType;
error?: any;
loading: boolean;
loaded: boolean;
hasCreate?: boolean;
Expand Down Expand Up @@ -57,25 +59,33 @@ export const useShowController = <RecordType extends Record = Record>(
props: ShowProps
): ShowControllerProps<RecordType> => {
useCheckMinimumRequiredProps('Show', ['basePath', 'resource'], props);
const { basePath, hasCreate, hasEdit, hasList, hasShow, id } = props;
const {
basePath,
hasCreate,
hasEdit,
hasList,
hasShow,
id,
onFailure,
} = props;
const resource = useResourceContext(props);
const translate = useTranslate();
const notify = useNotify();
const redirect = useRedirect();
const refresh = useRefresh();
const version = useVersion();
const { data: record, loading, loaded, refetch } = useGetOne<RecordType>(
resource,
id,
{
action: CRUD_GET_ONE,
onFailure: () => {
const { data: record, error, loading, loaded, refetch } = useGetOne<
RecordType
>(resource, id, {
action: CRUD_GET_ONE,
onFailure:
onFailure ??
(() => {
notify('ra.notification.item_doesnt_exist', 'warning');
redirect('list', basePath);
refresh();
},
}
);
}),
});

const getResourceLabel = useGetResourceLabel();
const defaultTitle = translate('ra.page.show', {
Expand All @@ -85,6 +95,7 @@ export const useShowController = <RecordType extends Record = Record>(
});

return {
error,
loading,
loaded,
defaultTitle,
Expand Down

0 comments on commit d84013a

Please sign in to comment.