Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
e3aecf1
add render props to ListBase component
ThieryMichel Jul 7, 2025
14652d4
add render props to InfiniteListBase component
ThieryMichel Jul 7, 2025
4619f16
add tests
ThieryMichel Jul 7, 2025
8ce9450
add render props to EditBase component
ThieryMichel Jul 7, 2025
6511b2e
add render props to CreateBase component
ThieryMichel Jul 7, 2025
bf3897d
add render props to ShowBase component
ThieryMichel Jul 7, 2025
e03818e
add render props to ReferenceFIeldBase component
ThieryMichel Jul 7, 2025
bcfa72a
add render prop on ReferenceManyFieldBase
ThieryMichel Jul 8, 2025
04b3085
add render prop on ReferenceOneFieldBase
ThieryMichel Jul 8, 2025
4e0ebea
create ReferenceArrayFieldBase and use it in MUI ReferenceArrayField
ThieryMichel Jul 8, 2025
730f556
add render prop on ReferenceArrayFieldBase
ThieryMichel Jul 8, 2025
3ae7f98
code review applied
ThieryMichel Jul 8, 2025
4d1c035
add check on render and children on all referenceBase field
ThieryMichel Jul 11, 2025
d385168
add doc for ReferenceFieldBase
ThieryMichel Jul 11, 2025
a78f838
add doc for ReferenceManyFieldBase
ThieryMichel Jul 11, 2025
665c58d
remove unneeded renderPagination prop from ReferenceManyFieldBase
ThieryMichel Jul 11, 2025
b39185d
remove unneeded renderPagination from ReferenceArrayFieldBase
ThieryMichel Jul 11, 2025
70b260c
add doc on ReferenceArrayFieldBase
ThieryMichel Jul 11, 2025
58b7a55
add doc on ReferenceManyFieldBase
ThieryMichel Jul 11, 2025
59bc05e
stop referring mui component in ReferenceFieldBase doc, move section …
ThieryMichel Jul 15, 2025
d7d1d0b
fix type
ThieryMichel Jul 15, 2025
8b256f5
rename ra-core FieldProps to FieldPropsBase
ThieryMichel Jul 15, 2025
68637c0
fix render type in doc
ThieryMichel Jul 15, 2025
60eaf37
fix ra-core/controller/field/ReferenceOneFieldBase story name
ThieryMichel Jul 17, 2025
8c08dec
code review
ThieryMichel Jul 17, 2025
8c95570
remove pagination prop from ReferenceArrayFieldBase and ReferenceMany…
ThieryMichel Jul 17, 2025
2f655d4
use context instead of controllerContext
ThieryMichel Jul 17, 2025
4a2d425
add missing pagination on mui reference many and array
ThieryMichel Jul 17, 2025
f3794b3
remove mui components from ReferenceManyFieldBase doc. Add example of…
ThieryMichel Jul 17, 2025
555d093
remove mui components from ReferenceFieldBase doc.
ThieryMichel Jul 17, 2025
9dbb604
remove mui components from ReferenceArrayFieldBase doc.
ThieryMichel Jul 17, 2025
f8f8465
remove mui components from ReferenceOneFieldBase doc.
ThieryMichel Jul 17, 2025
8046b0e
move pagination on ReferenceManyField side
ThieryMichel Jul 17, 2025
ab82156
Add optional loading, error and empty props to ReferenceBase commponents
fzaninotto Jul 21, 2025
ef70578
Improve ListBase doc
fzaninotto Jul 21, 2025
10a52cf
Misc doc fixes
fzaninotto Jul 21, 2025
a701965
Fix types
fzaninotto Jul 21, 2025
979444c
Fix base stories and doc
fzaninotto Jul 21, 2025
c6911a0
Add context to render empty, error and loading components
fzaninotto Jul 21, 2025
64d2806
Fix cannot override ReferenceArrayField props via theme anymore
fzaninotto Jul 21, 2025
0aba5d4
Improve readability
fzaninotto Jul 21, 2025
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
1 change: 1 addition & 0 deletions docs/CreateBase.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const BookCreate = () => (
You can customize the `<CreateBase>` component using the following props, documented in the `<Create>` component:

* `children`: the components that renders the form
* `render`: alternative to children, a function that takes the `CreateController` context and renders the form
* [`disableAuthentication`](./Create.md#disableauthentication): disable the authentication check
* [`mutationMode`](./Create.md#mutationmode): Switch to optimistic or undoable mutations (pessimistic by default)
* [`mutationOptions`](./Create.md#mutationoptions): options for the `dataProvider.create()` call
Expand Down
1 change: 1 addition & 0 deletions docs/EditBase.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export const BookEdit = () => (
You can customize the `<EditBase>` component using the following props, documented in the `<Edit>` component:

* `children`: the components that renders the form
* `render`: alternative to children, a function that takes the `EditController` context and renders the form
* [`disableAuthentication`](./Edit.md#disableauthentication): disable the authentication check
* [`id`](./Edit.md#id): the id of the record to edit
* [`mutationMode`](./Edit.md#mutationmode): switch to optimistic or pessimistic mutations (undoable by default)
Expand Down
66 changes: 46 additions & 20 deletions docs/ListBase.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,55 +6,81 @@ storybook_path: ra-core-controller-list-listbase--no-auth-provider

# `<ListBase>`

`<ListBase>` is a headless variant of [`<List>`](./List.md). It fetches a list of records from the data provider, puts it in a [`ListContext`](./useListContext.md), and renders its children. Use it to build a custom list layout.
`<ListBase>` is a headless List page component. It fetches a list of records from the data provider, puts it in a [`ListContext`](./useListContext.md), and renders its children. Use it to build a custom list layout.

Contrary to [`<List>`](./List.md), it does not render the page layout, so no title, no actions, no `<Card>`, and no pagination.

`<ListBase>` relies on the [`useListController`](./useListController.md) hook.

## Usage

You can use `ListBase` to create your own custom reusable List component, like this one:
You can use `ListBase` to create your own custom List page component, like this one:

```jsx
import {
DataTable,
ListBase,
Title,
ListToolbar,
Pagination,
DataTable,
Pagination,
Title,
} from 'react-admin';
import { Card } from '@mui/material';

const MyList = ({ children, actions, filters, title, ...props }) => (
<ListBase {...props}>
<Title title={title}/>
const PostList = () => (
<ListBase>
<Title title="Post List"/>
<ListToolbar
filters={filters}
actions={actions}
filters={[
{ source: 'q', label: 'Search', alwaysOn: true },
{ source: 'published', label: 'Published', type: 'boolean' },
]}
/>
<Card>
{children}
<DataTable>
<DataTable.Col source="title" />
<DataTable.Col source="author" />
<DataTable.Col source="published_at" />
</DataTable>
</Card>
<Pagination />
</ListBase>
);
```

Alternatively, you can pass a `render` function prop instead of `children`. This function will receive the `ListContext` as argument.

```jsx
const PostList = () => (
<MyList title="Post List">
<DataTable>
...
</DataTable>
</MyList>
<ListBase render={({ data, total, isPending, error }) => (
<Card>
<Title title="Post List" />
<ListToolbar
filters={[
{ source: 'q', label: 'Search', alwaysOn: true },
{ source: 'published', label: 'Published', type: 'boolean' },
]}
/>
<DataTable>
{data?.map(record => (
<DataTable.Row key={record.id}>
<DataTable.Col source="title" record={record} />
<DataTable.Col source="author" record={record} />
<DataTable.Col source="published_at" record={record} />
</DataTable.Row>
))}
</DataTable>
<Pagination total={total} />
</Card>
)} />
);
```

This custom List component has no aside component - it's up to you to add it in pure React.

## Props

The `<ListBase>` component accepts the same props as [`useListController`](./useListController.md):
The `<ListBase>` component accepts the following props:

* `children`
* [`debounce`](./List.md#debounce)
* [`disableAuthentication`](./List.md#disableauthentication)
* [`disableSyncWithLocation`](./List.md#disablesyncwithlocation)
Expand All @@ -63,13 +89,13 @@ The `<ListBase>` component accepts the same props as [`useListController`](./use
* [`filterDefaultValues`](./List.md#filterdefaultvalues)
* [`perPage`](./List.md#perpage)
* [`queryOptions`](./List.md#queryoptions)
* `render`
* [`resource`](./List.md#resource)
* [`sort`](./List.md#sort)

These are a subset of the props accepted by `<List>` - only the props that change data fetching, and not the props related to the user interface.

In addition, `<ListBase>` renders its children components inside a `ListContext`. Check [the `<List children>` documentation](./List.md#children) for usage examples.


## Security

The `<ListBase>` component requires authentication and will redirect anonymous users to the login page. If you want to allow anonymous access, use the [`disableAuthentication`](./List.md#disableauthentication) prop.
Expand Down
226 changes: 226 additions & 0 deletions docs/ReferenceArrayFieldBase.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
---
layout: default
title: "The ReferenceArrayFieldBase Component"
storybook_path: ra-core-fields-referencearrayfieldbase--basic
---

# `<ReferenceArrayFieldBase>`

Use `<ReferenceArrayFieldBase>` to display a list of related records, via a one-to-many relationship materialized by an array of foreign keys.

`<ReferenceArrayFieldBase>` fetches a list of referenced records (using the `dataProvider.getMany()` method), and puts them in a [`ListContext`](./useListContext.md). This component is headless, and its children need to use the data from this context to render the desired ui.

**Tip**: For a rendering a list of chips by default, use [the `<ReferenceArrayField>` component](./ReferenceArrayField.md) instead.

**Tip**: If the relationship is materialized by a foreign key on the referenced resource, use [the `<ReferenceManyFieldBase>` component](./ReferenceManyFieldBase.md) instead.

## Usage

For instance, let's consider a model where a `post` has many `tags`, materialized to a `tags_ids` field containing an array of ids:

```
┌──────────────┐ ┌────────┐
│ posts │ │ tags │
│--------------│ │--------│
│ id │ ┌───│ id │
│ title │ │ │ name │
│ body │ │ └────────┘
│ is_published │ │
│ tag_ids │╾──┘
└──────────────┘
```

A typical `post` record therefore looks like this:

```json
{
"id": 1,
"title": "Hello world",
"body": "...",
"is_published": true,
"tags_ids": [1, 2, 3]
}
```

In that case, use `<ReferenceArrayFieldBase>` to display the post tag names as a list of chips, as follows:

```jsx
import { ListBase, ListIterator, ReferenceArrayFieldBase } from 'react-admin';

export const PostList = () => (
<ListBase>
<ListIterator>
<ReferenceArrayFieldBase reference="tags" source="tag_ids">
<TagList />
</ReferenceArrayFieldBase>
</ListIterator>
</ListBase>
);

const TagList = (props: { children: React.ReactNode }) => {
const context = useListContext();

if (context.isPending) {
return <p>Loading...</p>;
}

if (context.error) {
return <p className="error">{context.error.toString()}</p>;
}
return (
<p>
{listContext.data?.map((tag, index) => (
<li key={index}>{tag.name}</li>
))}
</p>
);
};
```

`<ReferenceArrayFieldBase>` expects a `reference` attribute, which specifies the resource to fetch for the related records. It also expects a `source` attribute, which defines the field containing the list of ids to look for in the referenced resource.

`<ReferenceArrayFieldBase>` fetches the `tag` resources related to each `post` resource by matching `post.tag_ids` to `tag.id`.

You can change how the list of related records is rendered by passing a custom child reading the `ListContext` (e.g. a [`<DataTable>`](./DataTable.md)) or a render function prop. See the [`children`](#children) and the [`render`](#render) sections for details.

## Props

| Prop | Required | Type | Default | Description |
| -------------- | -------- | --------------------------------------------------------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `source` | Required | `string` | - | Name of the property to display |
| `reference` | Required | `string` | - | The name of the resource for the referenced records, e.g. 'tags' |
| `children` | Optional\* | `Element` | | One or several elements that render a list of records based on a `ListContext` |
| `render` | Optional\* | `(ListContext) => Element` | | A function that takes a list context and renders a list of records |
| `filter` | Optional | `Object` | - | Filters to use when fetching the related records (the filtering is done client-side) |
| `perPage` | Optional | `number` | 1000 | Maximum number of results to display |
| `queryOptions` | Optional | [`UseQuery Options`](https://tanstack.com/query/v5/docs/react/reference/useQuery) | `{}` | `react-query` options for the `getMany` query |
| `sort` | Optional | `{ field, order }` | `{ field: 'id', order: 'DESC' }` | Sort order to use when displaying the related records (the sort is done client-side) |
| `sortBy` | Optional | `string | Function` | `source` | When used in a `List`, name of the field to use for sorting when the user clicks on the column header. |

\* Either one of children or render is required.

## `children`

You can pass any React component as child, to render the list of related records based on the `ListContext`.

```jsx
<ReferenceArrayFieldBase label="Tags" reference="tags" source="tag_ids">
<TagList />
</ReferenceArrayFieldBase>

const TagList = (props: { children: React.ReactNode }) => {
const { isPending, error, data } = useListContext();

if (isPending) {
return <p>Loading...</p>;
}

if (error) {
return <p className="error">{error.toString()}</p>;
}
return (
<p>
{data?.map((tag, index) => (
<li key={index}>{tag.name}</li>
))}
</p>
);
};
```

## `render`

Alternatively to `children`, you can pass a `render` function prop to `<ReferenceArrayFieldBase>`. The `render` prop will receive the `ListContext` as its argument, allowing to inline the rendering logic.

```jsx
<ReferenceArrayFieldBase
label="Tags"
reference="tags"
source="tag_ids"
render={({ isPending, error, data }) => {
if (isPending) {
return <p>Loading...</p>;
}
if (error) {
return <p className="error">{error.toString()}</p>;
}
return (
<p>
{data.map((tag, index) => (
<li key={index}>{tag.name}</li>
))}
</p>
);
}}
/>
```

**Tip**: When receiving a `render` prop, the `<ReferenceArrayFieldBase>` component will ignore the `children` property.

## `filter`

`<ReferenceArrayFieldBase>` fetches all the related records, and displays them all, too. You can use the `filter` prop to filter the list of related records to display (this works by filtering the records client-side, after the fetch).

For instance, to render only tags that are 'published', you can use the following code:

{% raw %}
```jsx
<ReferenceArrayFieldBase
label="Tags"
source="tag_ids"
reference="tags"
filter={{ is_published: true }}
/>
```
{% endraw %}

## `perPage`

`<ReferenceArrayFieldBase>` fetches *all* the related fields, and puts them all in a `ListContext`. If a record has a large number of related records, it may be a good idea to limit the number of displayed records. The `perPage` prop allows to create a client-side pagination for the related records.

For instance, to limit the display of related records to 10, you can use the following code:

```jsx
<ReferenceArrayFieldBase label="Tags" source="tag_ids" reference="tags" perPage={10} />
```

## `queryOptions`

Use the `queryOptions` prop to pass options to [the `dataProvider.getMany()` query](./useGetOne.md#aggregating-getone-calls) that fetches the referenced record.

For instance, to pass [a custom `meta`](./Actions.md#meta-parameter):

{% raw %}
```jsx
<ReferenceArrayFieldBase queryOptions={{ meta: { foo: 'bar' } }} />
```
{% endraw %}

Comment thread
ThieryMichel marked this conversation as resolved.
## `reference`

The resource to fetch for the relateds record.

For instance, if the `posts` resource has a `tag_ids` field, set the `reference` to `tags` to fetch the tags related to each post.

```jsx
<ReferenceArrayFieldBase label="Tags" source="tag_ids" reference="tags" />
```

## `sort`

By default, the related records are displayed in the order in which they appear in the `source`. For instance, if the current record is `{ id: 1234, title: 'Lorem Ipsum', tag_ids: [1, 23, 4] }`, a `<ReferenceArrayFieldBase>` on the `tag_ids` field will display tags in the order 1, 23, 4.

`<ReferenceArrayFieldBase>` can force a different order (via a client-side sort after fetch) if you specify a `sort` prop.

For instance, to sort tags by title in ascending order, you can use the following code:

{% raw %}
```jsx
<ReferenceArrayFieldBase
label="Tags"
source="tag_ids"
reference="tags"
sort={{ field: 'title', order: 'ASC' }}
/>
```
{% endraw %}
Loading
Loading