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

Returns error from controllers & add support for custom onFailure on useShowController #6680

Merged
merged 2 commits into from
Oct 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
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
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