Skip to content

Commit c4be146

Browse files
authored
Merge pull request #5886 from marmelab/reference-array-input-list-context
Add ListContext to ReferenceArrayInput
2 parents 64e8471 + 59e4097 commit c4be146

13 files changed

+877
-306
lines changed

docs/Inputs.md

+23-1
Original file line numberDiff line numberDiff line change
@@ -1406,7 +1406,7 @@ http://myapi.com/tags?id=[1,23,4]
14061406
http://myapi.com/tags?page=1&perPage=25
14071407
```
14081408

1409-
Once it receives the deduplicated reference resources, this component delegates rendering to a subcomponent, to which it passes the possible choices as the `choices` attribute.
1409+
Once it receives the deduplicated reference resources, this component delegates rendering to a subcomponent, by providing the possible choices through the `ReferenceArrayInputContext`. This context value can be accessed with the [`useReferenceArrayInputContext`](#usereferencearrayinputcontext) hook.
14101410

14111411
This means you can use `<ReferenceArrayInput>` with [`<SelectArrayInput>`](#selectarrayinput), or with the component of your choice, provided it supports the `choices` attribute.
14121412

@@ -1477,8 +1477,30 @@ You can tweak how this component fetches the possible values using the `perPage`
14771477
```
14781478
{% endraw %}
14791479

1480+
In addition to the `ReferenceArrayInputContext`, `<ReferenceArrayInput>` also sets up a `ListContext` providing access to the records from the reference resource in a similar fashion to that of the `<List>` component. This `ListContext` value is accessible with the [`useListContext`](/List.md#uselistcontext) hook.
1481+
14801482
`<ReferenceArrayInput>` also accepts the [common input props](./Inputs.md#common-input-props).
14811483

1484+
### `useReferenceArrayInputContext`
1485+
1486+
The [`<ReferenceArrayInput>`](#referencearrayinput) component take care of fetching the data, and put that data in a context called `ReferenceArrayInputContext` so that it’s available for its descendants. This context also stores filters, pagination, sort state, and provides callbacks to update them.
1487+
1488+
Any component decendent of `<ReferenceArryInput>` can grab information from the `ReferenceArrayInputContext` using the `useReferenceArrayInputContext` hook. Here is what it returns:
1489+
1490+
```js
1491+
const {
1492+
choices, // An array of records matching both the current input value and the filters
1493+
error, // A potential error that may have occured while fetching the data
1494+
warning, // A potential warning regarding missing references
1495+
loaded, // boolean that is false until the data is available
1496+
loading, // boolean that is true on mount, and false once the data was fetched
1497+
setFilter, // a callback to update the filters, e.g. setFilters({ q: 'query' })
1498+
setPagination, // a callback to change the pagination, e.g. setPagination({ page: 2, perPage: 50 })
1499+
setSort, // a callback to change the sort, e.g. setSort({ field: 'name', order: 'DESC' })
1500+
setSortForList, // a callback to set the sort with the same signature as the one from the ListContext. This is required to avoid breaking backward compatibility and will be removed in v4
1501+
} = useReferenceArrayInputContext();
1502+
```
1503+
14821504
### `<ReferenceInput>`
14831505

14841506
Use `<ReferenceInput>` for foreign-key values, for instance, to edit the `post_id` of a `comment` resource. This component fetches the related record (using `dataProvider.getMany()`) as well as possible choices (using `dataProvider.getList()` in the reference resource), then delegates rendering to a subcomponent, to which it passes the possible choices as the `choices` attribute.

packages/ra-core/src/controller/field/useReferenceArrayFieldController.ts

+1-8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import usePaginationState from '../usePaginationState';
1111
import useSelectionState from '../useSelectionState';
1212
import useSortState from '../useSortState';
1313
import { useResourceContext } from '../../core';
14+
import { indexById } from '../../util/indexById';
1415

1516
interface Option {
1617
basePath: string;
@@ -239,12 +240,4 @@ const useReferenceArrayFieldController = (
239240
};
240241
};
241242

242-
const indexById = (records: Record[] = []): RecordMap =>
243-
records
244-
.filter(r => typeof r !== 'undefined')
245-
.reduce((prev, current) => {
246-
prev[current.id] = current;
247-
return prev;
248-
}, {});
249-
250243
export default useReferenceArrayFieldController;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { createContext } from 'react';
2+
import { PaginationPayload, Record, SortPayload } from '../../types';
3+
4+
/**
5+
* Context which provides access to the useReferenceArrayInput features.
6+
*
7+
* @example
8+
* const ReferenceArrayInput = ({ children }) => {
9+
* const controllerProps = useReferenceArrayInputController();
10+
* return (
11+
* <ReferenceArrayInputContextProvider value={controllerProps}>
12+
* {children}
13+
* </ReferenceArrayInputContextProvider>
14+
* )
15+
* }
16+
*/
17+
export const ReferenceArrayInputContext = createContext(undefined);
18+
19+
export interface ReferenceArrayInputContextValue {
20+
choices: Record[];
21+
error?: any;
22+
warning?: any;
23+
loading: boolean;
24+
loaded: boolean;
25+
setFilter: (filter: any) => void;
26+
setPagination: (pagination: PaginationPayload) => void;
27+
setSort: (sort: SortPayload) => void;
28+
setSortForList: (sort: string, order?: string) => void;
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import * as React from 'react';
2+
import { ReactNode } from 'react';
3+
import {
4+
ReferenceArrayInputContext,
5+
ReferenceArrayInputContextValue,
6+
} from './ReferenceArrayInputContext';
7+
8+
/**
9+
* Provider for the context which provides access to the useReferenceArrayInput features.
10+
*
11+
* @example
12+
* const ReferenceArrayInput = ({ children }) => {
13+
* const controllerProps = useReferenceArrayInputController();
14+
* return (
15+
* <ReferenceArrayInputContextProvider value={controllerProps}>
16+
* {children}
17+
* </ReferenceArrayInputContextProvider>
18+
* )
19+
* }
20+
*/
21+
export const ReferenceArrayInputContextProvider = ({
22+
children,
23+
value,
24+
}: {
25+
children: ReactNode;
26+
value: ReferenceArrayInputContextValue;
27+
}) => (
28+
<ReferenceArrayInputContext.Provider value={value}>
29+
{children}
30+
</ReferenceArrayInputContext.Provider>
31+
);

0 commit comments

Comments
 (0)