diff --git a/docs/CreateEdit.md b/docs/CreateEdit.md index b7a58dc22a0..dd7ff7d2750 100644 --- a/docs/CreateEdit.md +++ b/docs/CreateEdit.md @@ -373,6 +373,113 @@ React-admin provides guessers for the `List` view (`ListGuesser`), the `Edit` vi **Tip**: Do not use the guessers in production. They are slower than manually-defined components, because they have to infer types based on the content. Besides, the guesses are not always perfect. +## `useCreateController` and `useEditController` + +The `` and `` components both take care of two things: + +1. (the "controller") Fetching data based on the URL and transforming it +2. (the "view") Rendering the page title, the actions, the content and aside areas + +In some cases, you may want to customize the view entirely (i.e. keep the code for step 1, and provide your own code for step 2). For these cases, react-admin provides two hooks, `useCreateController()` and `useEditController()`. These hooks contain just the controller part of the `` and `` components. + +**Tip**: You should not use these hooks to hide or show form inputs based on the data. For that need, check [``](./Inputs.md#linking-two-inputs) + +### `useCreateController` + +This hook takes one object as input (the props passed to a `` component) and returns the save callback for the Create view, as well as some pre-computed values. You can use it to create your own custom Create view, like this one: + +```jsx +import { useCreateController, SimpleForm } from 'react-admin'; + +const MyCreate = props => { + const { + basePath, // deduced from the location, useful for action buttons + defaultTitle, // the translated title based on the resource, e.g. 'Create Post' + record, // empty object, unless some values were passed in the location state to prefill the form + redirect, // the default redirection route. Defaults to 'edit', unless the resource has no edit view, in which case it's 'list' + resource, // the resource name, deduced from the location. e.g. 'posts' + save, // the create callback, to be passed to the underlying form as submit handler + saving, // boolean that becomes true when the dataProvider is called to create the record + version, // integer used by the refresh feature + } = useCreateController(props); + return ( +
+

{defaultTitle}

+ {cloneElement(props.children, { + basePath, + record, + redirect, + resource, + save, + saving, + version, + })} +
+ ); +} + +const PostCreate = props => ( + + + ... + + +) +``` + +This custom Create view has no action buttons or aside component - it's up to you to add them in pure React. + +**Tip**: You don't have to clone the child element. If you can't reuse an existing form component like `` or ``, feel free to write the form code inside your custom `MyCreate` component. + +### `useEditController` + +This hook takes one object as input (the props passed to an `` component) and returns the fetched data and callbacks for the Edit view. You can use it to create your own custom Edit view, like this one: + +```jsx +import { useEditController, SimpleForm } from 'react-admin'; + +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' + 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 + redirect, // the default redirection route. Defaults to 'list' + resource, // the resource name, deduced from the location. e.g. 'posts' + save, // the update callback, to be passed to the underlying form as submit handler + saving, // boolean that becomes true when the dataProvider is called to update the record + version, // integer used by the refresh feature + } = useEditController(props); + return ( +
+

{defaultTitle}

+ {cloneElement(props.children, { + basePath, + record, + redirect, + resource, + save, + saving, + version, + })} +
+ ); +} + +const PostEdit = props => ( + + + ... + + +) +``` + +This custom Edit view has no action buttons or aside component - it's up to you to add them in pure React. + +**Tip**: You don't have to clone the child element. If you can't reuse an existing form component like `` or ``, feel free to write the form code inside your custom `MyEdit` component. + ## The `` component The `` component receives the `record` as prop from its parent component. It is responsible for rendering the actual form. It is also responsible for validating the form data. Finally, it receives a `handleSubmit` function as prop, to be called with the updated record as argument when the user submits the form. diff --git a/docs/List.md b/docs/List.md index 1cf9721b889..c474d20d85d 100644 --- a/docs/List.md +++ b/docs/List.md @@ -797,6 +797,96 @@ React-admin provides guessers for the `List` view (`ListGuesser`), the `Edit` vi **Tip**: Do not use the guessers in production. They are slower than manually-defined components, because they have to infer types based on the content. Besides, the guesses are not always perfect. +## `useListController` + +The `` components takes care of two things: + +1. (the "controller") Fetching data based on the URL and transforming it +2. (the "view") Rendering the page title, the actions, the content and aside areas + +In some cases, you may want to customize the view entirely (i.e. keep the code for step 1, and provide your own code for step 2). For these cases, react-admin provides a hook called `useListController()`, which contain just the controller part of the ``. + +This hook takes one object as input (the props passed to a `` component) and returns the fetched data and callbacks for the List view. In fact, it returns a lot of variables because the List page is complex: based on the URL, the List controller deduces filters, pagination, ordering, it provides callbacks to update them, it fetches the data, etc. + +You can use `useListController()` to create your own custom List view, like this one: + +```jsx +import { + useListController, + ListToolbar, + BulkActionsToolbar, + Pagination, + Datagrid +} from 'react-admin'; + +const MyList = props => { + const controllerProps = useListController(props); + const { + // fetched data + data, // an id-based dictionary of the list data, e.g. { 123: { id: 123, title: 'hello world' }, 456: { ... } } + ids, // an array listing the ids of the records in the list, e.g [123, 456, ...] + total, // the total number of results for the current filters, excluding pagination. Useful to build the pagination controls. e.g. 23 + loaded, // boolean that is false until the data is available + loading, // boolean that is true on mount, and false once the data was fetched + // pagination + page, // the current page. Starts at 1 + perPage, // the number of results per page. Defaults to 25 + setPage, // a callback to change the page, e.g. setPage(3) + setPerPage, // a callback to change the number of results per page, e.g. setPerPage(25) + // sorting + currentSort, // a sort object { field, order }, e.g. { field: 'date', order: 'DESC' } + setSort, // a callback to change the sort, e.g. setSort('name', 'ASC') + // filtering + displayedFilters, // a dictionary of the displayed filters, e.g. { title: true, nationality: true } + filterValues, // a dictionary of filter values, e.g. { title: 'lorem', nationality: 'fr' } + setFilters, // a callback to update the filters, e.g. setFilters(filters, displayedFilters) + showFilter, // a callback to show one of the filters, e.g. showFilter('title', defaultValue) + hideFilter, // a callback to hide one of the filters, e.g. hidefilter('title') + // row selection + selectedIds, // an array listing the ids of the selcted rows, e.g. [123, 456] + onSelect, // callback to change the list of selected rows, e.g onSelect([456, 789]) + onToggleItem, // callback to toggle the selection of a given record based on its id, e.g. onToggleItem(456) + onUnselectItems, // callback to clear the selection, e.g. onUnselectItems(); + // misc + basePath, // deduced from the location, useful for action buttons + defaultTitle, // the translated title based on the resource, e.g. 'Posts' + resource, // the resource name, deduced from the location. e.g. 'posts' + version, // integer used by the refresh feature + } = controllerProps; + return ( +
+

{defaultTitle}

+ + + {props.bulkActionButtons} + + {cloneElement(children, { + ...controllerProps, + hasBulkActions: props.bulkActionButtons !== false, + })} + +
+ ); +} + +const PostList = props => ( + + + ... + + +) +``` + +This custom List view has no aside component - it's up to you to add it in pure React. + +**Tip**: You don't have to clone the child element. If you can't reuse an existing list view component like `` or ``, feel free to write the form code inside your custom `MyList` component. + ## The `` component The `Datagrid` component renders a list of records as a table. It is usually used as a child of the [``](#the-list-component) and [``](./Fields.md#referencemanyfield) components. diff --git a/docs/Reference.md b/docs/Reference.md index a0e7bebb16a..7e1271a3d6b 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -106,11 +106,11 @@ title: "Reference" * `useCheckAuth` * `useChoices` * [`useCreate`](./Actions.md#specialized-hooks) -* `useCreateController` +* [`useCreateController`](./CreateEdit.md#usecreatecontroller) * [`useDataProvider`](./Actions.md#usedataprovider-hook) * [`useDelete`](./Actions.md#specialized-hooks) * [`useDeleteMany`](./Actions.md#specialized-hooks) -* `useEditController` +* [`useEditController`](./CreateEdit.md#useeditcontroller) * `useFilterState` * [`useGetList`](./Actions.md#specialized-hooks) * [`useGetMany`](./Actions.md#specialized-hooks) @@ -120,7 +120,7 @@ title: "Reference" * [`useGetOne`](./Actions.md#specialized-hooks) * `useGetPermissions` * `useInput` -* `useListController` +* [`useListController`](./List.md#uselistcontroller) * `useListParams` * `useLoading` * [`useLocale`](./Translation.md#uselocale-getting-the-current-locale) @@ -142,7 +142,7 @@ title: "Reference" * `useReferenceManyFieldController` * [`useRefresh`](./Actions.md#handling-side-effects-in-usedataprovider) * [`useSetLocale`](./Translation.md#usesetlocale-changing-locale-at-runtime) -* `useShowController` +* [`useShowController`](./Show.md#useshowcontroller) * `useSortState` * [`useStyles`](./Theming.md#overriding-a-component-style) * `useSuggestions` diff --git a/docs/Show.md b/docs/Show.md index 2ceb5aeb71e..b8f519871f0 100644 --- a/docs/Show.md +++ b/docs/Show.md @@ -201,6 +201,56 @@ React-admin provides guessers for the `List` view (`ListGuesser`), the `Edit` vi **Tip**: Do not use the guessers in production. They are slower than manually-defined components, because they have to infer types based on the content. Besides, the guesses are not always perfect. +## `useShowController` + +The `` component takes care of two things: + +1. (the "controller") Fetching data based on the URL and transforming it +2. (the "view") Rendering the page title, the actions, the content and aside areas + +In some cases, you may want to customize the view entirely (i.e. keep the code for step 1, and provide your own code for step 2). For these cases, react-admin provides a hook called `useShowController()`, which contains just the controller part of the `` component. + +This hook takes one object as input (the props passed to a `` component) and returns the fetched data for the Show view. You can use it to create your own custom Show view, like this one: + +```jsx +import { useShowController, SimpleShowLayout } from 'react-admin'; + +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' + 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 + resource, // the resource name, deduced from the location. e.g. 'posts' + version, // integer used by the refresh feature + } = useShowController(props); + return ( +
+

{defaultTitle}

+ {cloneElement(props.children, { + basePath, + record, + resource, + version, + })} +
+ ); +} + +const PostShow = props => ( + + + ... + + +) +``` + +This custom Show view has no action buttons or aside component - it's up to you to add them in pure React. + +**Tip**: You don't have to clone the child element. If you can't reuse an existing form component like ``, feel free to write the form code inside your custom `MyShow` component. + ## The `` component The `` component receives the `record` as prop from its parent component. It is responsible for rendering the actual view. diff --git a/docs/navigation.html b/docs/navigation.html index 7c9884b8b50..48e444fc0dd 100644 --- a/docs/navigation.html +++ b/docs/navigation.html @@ -11,12 +11,12 @@
  • <List> View
  • -
  • <Show> - View
  • <Create> and <Edit> Views
  • +
  • <Show> + View
  • <Field> Components