Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add ListContext to ReferenceArrayInput #5886

Merged
merged 16 commits into from
Feb 24, 2021
24 changes: 23 additions & 1 deletion docs/Inputs.md
Original file line number Diff line number Diff line change
Expand Up @@ -1406,7 +1406,7 @@ http://myapi.com/tags?id=[1,23,4]
http://myapi.com/tags?page=1&perPage=25
```

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.
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.

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

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

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.

`<ReferenceArrayInput>` also accepts the [common input props](./Inputs.md#common-input-props).

### `useReferenceArrayInputContext`

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.

Any component decendent of `<ReferenceArryInput>` can grab information from the `ReferenceArrayInputContext` using the `useReferenceArrayInputContext` hook. Here is what it returns:

```js
const {
choices, // An array of records matching both the current input value and the filters
error, // A potential error that may have occured while fetching the data
warning, // A potential warning regarding missing references
loaded, // boolean that is false until the data is available
loading, // boolean that is true on mount, and false once the data was fetched
setFilter, // a callback to update the filters, e.g. setFilters({ q: 'query' })
setPagination, // a callback to change the pagination, e.g. setPagination({ page: 2, perPage: 50 })
setSort, // a callback to change the sort, e.g. setSort({ field: 'name', order: 'DESC' })
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
} = useReferenceArrayInputContext();
```

### `<ReferenceInput>`

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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import usePaginationState from '../usePaginationState';
import useSelectionState from '../useSelectionState';
import useSortState from '../useSortState';
import { useResourceContext } from '../../core';
import { indexById } from '../../util/indexById';

interface Option {
basePath: string;
Expand Down Expand Up @@ -239,12 +240,4 @@ const useReferenceArrayFieldController = (
};
};

const indexById = (records: Record[] = []): RecordMap =>
records
.filter(r => typeof r !== 'undefined')
.reduce((prev, current) => {
prev[current.id] = current;
return prev;
}, {});

export default useReferenceArrayFieldController;
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { createContext } from 'react';
import { PaginationPayload, Record, SortPayload } from '../../types';

/**
* Context which provides access to the useReferenceArrayInput features.
*
* @example
* const ReferenceArrayInput = ({ children }) => {
* const controllerProps = useReferenceArrayInputController();
* return (
* <ReferenceArrayInputContextProvider value={controllerProps}>
* {children}
* </ReferenceArrayInputContextProvider>
* )
* }
*/
export const ReferenceArrayInputContext = createContext(undefined);

export interface ReferenceArrayInputContextValue {
choices: Record[];
error?: any;
warning?: any;
loading: boolean;
loaded: boolean;
setFilter: (filter: any) => void;
setPagination: (pagination: PaginationPayload) => void;
setSort: (sort: SortPayload) => void;
setSortForList: (sort: string, order?: string) => void;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import * as React from 'react';
import { ReactNode } from 'react';
import {
ReferenceArrayInputContext,
ReferenceArrayInputContextValue,
} from './ReferenceArrayInputContext';

/**
* Provider for the context which provides access to the useReferenceArrayInput features.
*
* @example
* const ReferenceArrayInput = ({ children }) => {
* const controllerProps = useReferenceArrayInputController();
* return (
* <ReferenceArrayInputContextProvider value={controllerProps}>
* {children}
* </ReferenceArrayInputContextProvider>
* )
* }
*/
export const ReferenceArrayInputContextProvider = ({
children,
value,
}: {
children: ReactNode;
value: ReferenceArrayInputContextValue;
}) => (
<ReferenceArrayInputContext.Provider value={value}>
{children}
</ReferenceArrayInputContext.Provider>
);
Loading