Skip to content

Commit fd141cd

Browse files
authored
Merge pull request #8917 from marmelab/WithListContext
Add WithListContext component
2 parents 1f22074 + 1945254 commit fd141cd

21 files changed

+669
-72
lines changed

docs/ArrayField.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,46 @@ const PostShow = () => (
112112
</ArrayField>
113113
```
114114

115+
You can also render custom JSX, leveraging [the `<WithListContext>` component](./WithListContext.md):
116+
117+
```jsx
118+
<ArrayField source="backlinks">
119+
<WithListContext render={({ data }) => (
120+
<ul>
121+
{data.map(backlink => (
122+
<li key={backlink.id}>{backlink.url}</li>
123+
))}
124+
</ul>
125+
)} />
126+
</ArrayField>
127+
```
128+
129+
Or a custom component, leveraging [the `useListContext` hook](./useListContext.md):
130+
131+
```jsx
132+
const Backlinks = () => {
133+
const { data } = useListContext();
134+
return (
135+
<ul>
136+
{data.map(backlink => (
137+
<li key={backlink.id}>{backlink.url}</li>
138+
))}
139+
</ul>
140+
);
141+
};
142+
143+
const PostShow = () => (
144+
<Show>
145+
<SimpleShowLayout>
146+
<TextField source="title" />
147+
<ArrayField source="backlinks">
148+
<Backlinks />
149+
</ArrayField>
150+
</SimpleShowLayout>
151+
</Show>
152+
)
153+
```
154+
115155
## `filter`
116156

117157
You can use the `filter` prop to display only a subset of the items in the array. For instance, to display only the backlinks for a particular day:

docs/ListBase.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,6 @@ The `<ListBase>` component accepts the same props as [`useListController`](./use
6363
* [`resource`](./List.md#resource)
6464
* [`sort`](./List.md#sort-default-sort-field--order)
6565

66-
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.
66+
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.
67+
68+
In addition, `<ListBase>` renders its children components inside a `ListContext`. Check [the `<List children>` documentation](./List.md#children-list-layout) for usage examples.

docs/ListTutorial.md

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -545,14 +545,39 @@ Check [the Theming documentation](./Theming.md) for more information about the `
545545

546546
## Building a Custom Iterator
547547

548-
In some cases, neither the `<Datagrid>` nor the `<SimpleList>` components allow to display the records in an optimal way for a given task. In these cases, pass your layout component directly as children of the `<List>` component. As `<List>` takes care of fetching the data and putting it in a `ListContext`, you can leverage [the `useListContext` hook](./useListContext.md) to get the list data.
548+
In some cases, neither the `<Datagrid>` nor the `<SimpleList>` components allow to display the records in an optimal way for a given task. In these cases, pass your layout component directly as children of the `<List>` component.
549+
550+
As `<List>` takes care of fetching the data and putting it in a `ListContext`, you can leverage [the `<WithListContext>` component](./WithListContext.md) to get the list data in a render prop.
551+
552+
{% raw %}
553+
```jsx
554+
import { List, WithListContext } from 'react-admin';
555+
import { Stack, Typography } from '@mui/material';
556+
557+
const BookList = () => (
558+
<List emptyWhileLoading>
559+
<WithListContext render={({ data }) => (
560+
<Stack spacing={2} sx={{ padding: 2 }}>
561+
{data.map(book => (
562+
<Typography key={book.id}>
563+
<i>{book.title}</i>, by {book.author} ({book.year})
564+
</Typography>
565+
))}
566+
</Stack>
567+
)} />
568+
</List>
569+
);
570+
```
571+
{% endraw %}
572+
573+
If you prefer using a hook, you can use [the `useListContext` hook](./useListContext.md) instead:
549574

550575
{% raw %}
551576
```jsx
552577
import { List, useListContext } from 'react-admin';
553578
import { Stack, Typography } from '@mui/material';
554579

555-
const SimpleBookList = () => {
580+
const BookListView = () => {
556581
const { data } = useListContext();
557582
return (
558583
<Stack spacing={2} sx={{ padding: 2 }}>
@@ -563,12 +588,11 @@ const SimpleBookList = () => {
563588
))}
564589
</Stack>
565590
);
566-
}
591+
};
567592

568-
// use the custom list layout as <List> child
569593
const BookList = () => (
570594
<List emptyWhileLoading>
571-
<SimpleBookList />
595+
<BookListView />
572596
</List>
573597
);
574598
```

docs/Reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ title: "Index"
189189
* [`<UserMenu>`](./AppBar.md#usermenu)
190190

191191
**- W -**
192+
* [`<WithListContext>`](./WithListContext.md)
192193
* [`<WithPermissions>`](./WithPermissions.md)
193194
* [`<WithRecord>`](./WithRecord.md)
194195
* [`<WizardForm>`](./WizardForm.md)<img class="icon" src="./img/premium.svg" />

docs/ReferenceArrayField.md

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ You can change how the list of related records is rendered by passing a custom c
9696

9797
By default, `<ReferenceArrayField>` renders one string by related record, via a [`<SingleFieldList>`](./SingleFieldList.md) with a [`<ChipField>`](./ChipField.md) using the resource [`recordRepresentation`](./Resource.md#recordrepresentation).
9898

99+
![ReferenceArrayField with default children](./img/ReferenceArrayField-default-child.png)
100+
99101
You can pass any component of your own as child, to render the list of related records in another way.
100102

101103
That means that using the field without child:
@@ -121,16 +123,15 @@ Is equivalent to:
121123
- [`<SimpleList>`](./SimpleList.md)
122124
- [`<EditableDatagrid>`](./EditableDatagrid.md)
123125
- [`<Calendar>`](./Calendar.md)
124-
- Or a component of your own (check the [`usListContext`](./useListContext.md) chapter to learn how).
126+
- Or a component of your own (check the [`<WithListContext>`](./WithListContext.md) and the [`useListContext`](./useListContext.md) chapters to learn how).
125127

126128
For instance, use a `<Datagrid>` to render the related records in a table:
127129

128130
```jsx
129-
import * as React from "react";
130131
import { Show, SimpleShowLayout, TextField, ReferenceArrayField, Datagrid, ShowButton } from 'react-admin';
131132

132-
export const PostShow = (props) => (
133-
<Show {...props}>
133+
export const PostShow = () => (
134+
<Show>
134135
<SimpleShowLayout>
135136
<TextField source="id" />
136137
<TextField source="title" />
@@ -147,6 +148,31 @@ export const PostShow = (props) => (
147148
);
148149
```
149150

151+
Or [`<WithListContext>`](./WithListContext.md) to render the related records in a custom way:
152+
153+
```jsx
154+
import { Show, SimpleShowLayout, TextField, ReferenceArrayField, WithListContext } from 'react-admin';
155+
156+
export const PostShow = () => (
157+
<Show>
158+
<SimpleShowLayout>
159+
<TextField source="id" />
160+
<TextField source="title" />
161+
<ReferenceArrayField label="Tags" reference="tags" source="tag_ids">
162+
<WithListContext render={({ data }) => (
163+
<ul>
164+
{data.map(tag => (
165+
<li key={tag.id}>{tag.name}</li>
166+
))}
167+
</ul>
168+
)} />
169+
</ReferenceArrayField>
170+
<EditButton />
171+
</SimpleShowLayout>
172+
</Show>
173+
);
174+
```
175+
150176
## `filter`
151177

152178
`<ReferenceArrayField>` 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).

docs/ReferenceManyField.md

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export const AuthorShow = () => (
7373

7474
`<ReferenceManyField>` accepts a `reference` attribute, which specifies the resource to fetch for the related record. It also accepts a `source` attribute which defines the field containing the value to look for in the `target` field of the referenced resource. By default, this is the `id` of the resource (`authors.id` in the previous example).
7575

76-
You can also use `<ReferenceManyField>` in a list, e.g. to display the authors of the comments related to each post in a list by matching `post.id` to `comment.post_id`. We're using `<SingleFieldList>` to display an inline list using only one field for each of the referenced record:
76+
You can also use `<ReferenceManyField>` in a list, e.g. to display the authors of the comments related to each post in a list by matching `post.id` to `comment.post_id`:
7777

7878
```jsx
7979
import * as React from "react";
@@ -97,6 +97,8 @@ export const PostList = () => (
9797

9898
![ReferenceManyFieldSingleFieldList](./img/reference-many-field-single-field-list.png)
9999

100+
This example leverages [`<SingleFieldList>`](./SingleFieldList.md) to display an inline list using only one field for each of the referenced records.
101+
100102
## Props
101103

102104
| Prop | Required | Type | Default | Description |
@@ -112,6 +114,64 @@ export const PostList = () => (
112114

113115
`<ReferenceManyField>` also accepts the [common field props](./Fields.md#common-field-props), except `emptyText` (use the child `empty` prop instead).
114116

117+
## `children`
118+
119+
`<ReferenceManyField>` renders its children inside a [`ListContext`](./useListContext.md). This means you can use any component that uses a `ListContext`:
120+
121+
- [`<SingleFieldList>`](./SingleFieldList.md)
122+
- [`<Datagrid>`](./Datagrid.md)
123+
- [`<SimpleList>`](./SimpleList.md)
124+
- [`<EditableDatagrid>`](./EditableDatagrid.md)
125+
- [`<Calendar>`](./Calendar.md)
126+
- Or a component of your own (check the [`<WithListContext>`](./WithListContext.md) and the [`useListContext`](./useListContext.md) chapters to learn how).
127+
128+
For instance, use a `<Datagrid>` to render the related records in a table:
129+
130+
```jsx
131+
import { Show, SimpleShowLayout, TextField, ReferenceManyField, Datagrid } from 'react-admin';
132+
133+
export const AuthorShow = () => (
134+
<Show>
135+
<SimpleShowLayout>
136+
<TextField source="first_name" />
137+
<TextField source="last_name" />
138+
<DateField label="Born" source="dob" />
139+
<ReferenceManyField label="Books" reference="books" target="author_id">
140+
<Datagrid>
141+
<TextField source="title" />
142+
<TextField source="year" />
143+
</Datagrid>
144+
</ReferenceManyField>
145+
</SimpleShowLayout>
146+
</Show>
147+
);
148+
```
149+
150+
Or [`<WithListContext>`](./WithListContext.md) to render the related records in a custom way:
151+
152+
```jsx
153+
import { Show, SimpleShowLayout, TextField, ReferenceManyField, WithListContext } from 'react-admin';
154+
155+
export const AuthorShow = () => (
156+
<Show>
157+
<SimpleShowLayout>
158+
<TextField source="first_name" />
159+
<TextField source="last_name" />
160+
<DateField label="Born" source="dob" />
161+
<ReferenceManyField label="Books" reference="books" target="author_id">
162+
<WithListContext render={({ data }) => (
163+
<ul>
164+
{data.map(book => (
165+
<li key={book.id}>{book.title}</li>
166+
))}
167+
</ul>
168+
)} />
169+
</ReferenceManyField>
170+
</SimpleShowLayout>
171+
</Show>
172+
);
173+
```
174+
115175
## `filter`
116176

117177
You can filter the query used to populate the possible values. Use the `filter` prop for that.

docs/SingleFieldList.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ title: "The SingleFieldList Component"
77

88
Use `<SingleFieldList>` when you want to display only one property for each record in a list, for instance, to display the list of tag names for a post.
99

10-
![SingleFieldList](./img/singlefieldlist.png)
10+
![SingleFieldList](./img/ReferenceArrayField-default-child.png)
1111

1212
`<SingleFieldList>` is an **iterator** component: it gets `data` from the `ListContext`, and iterates over it to display each record. It creates a `<RecordContext>` for each record, and delegates the actual rendering to its child - usually a Field component.
1313

0 commit comments

Comments
 (0)