diff --git a/docs/AutocompleteArrayInput.md b/docs/AutocompleteArrayInput.md index 54a05903a35..459e2d0e19c 100644 --- a/docs/AutocompleteArrayInput.md +++ b/docs/AutocompleteArrayInput.md @@ -310,52 +310,77 @@ If a prompt is not enough, you can use [the `create` prop](#create) to render a ## `optionText` -You can customize the properties to use for the option name (instead of the default `name`) thanks to the `optionText` prop: +By default, `` uses the `name` property as the text content of each option. ```jsx -const choices = [ - { id: 'admin', label: 'Admin' }, - { id: 'u001', label: 'Editor' }, - { id: 'u002', label: 'Moderator' }, - { id: 'u003', label: 'Reviewer' }, -]; - +import { AutocompleteArrayInput } from 'react-admin'; + + +// renders the following list of choices +// - Tech +// - Lifestyle +// - People ``` -`optionText` is especially useful when the choices are records coming from a `` or a ``. By default, react-admin uses the [`recordRepresentation`](./Resource.md#recordrepresentation) function to display the record label. But if you set the `optionText` prop, react-admin will use it instead. +If your `choices` don't have a `name` property, or if you want to use another property, you can use the `optionText` prop to specify which property to use: ```jsx - - - + ``` -`optionText` also accepts a function, so you can shape the option text based on the entire choice object: +`optionText` also accepts a function, so you can shape the option text at will: ```jsx const choices = [ { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, { id: 456, first_name: 'Jane', last_name: 'Austen' }, ]; + +// Note we declared the function outside the component to avoid rerenders const optionRenderer = choice => `${choice.first_name} ${choice.last_name}`; ``` -`optionText` also accepts a React Element, that will be rendered inside a [``](./useRecordContext.md) using the related choice as the `record` prop. You can use Field components there. +**Tip**: Make sure you provide a stable reference to the function passed as `optionText`. Either declare it outside the component render function or wrap it inside a [`useCallback`](https://react.dev/reference/react/useCallback). + +`optionText` also accepts a React Element, that will be rendered inside a [``](./useRecordContext.md) using the related choice as the `record` prop. You can use Field components there. However, using an element as `optionText` implies that you also set two more props, `inputText` and `matchSuggestion`. See [Using A Custom Element For Options](#using-a-custom-element-for-options) for more details. + +`optionText` is also useful when the choices are records [fetched from another resource](#fetching-choices), and `` is a child of a [``](./ReferenceArrayInput.md). ```jsx -const choices = [ - { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, - { id: 456, first_name: 'Jane', last_name: 'Austen' }, -]; +import { AutocompleteArrayInput, ReferenceArrayInput } from 'react-admin'; -const FullNameField = () => { - const record = useRecordContext(); - return {record.first_name} {record.last_name}; -} + + + +``` + +In that case, react-admin uses the [`recordRepresentation`](./Resource.md#recordrepresentation) of the related resource to display the record label. In the example above, `` uses the resource representation of the `authors` resource, which is the `name` property. + +But if you set the `optionText` prop, react-admin uses it instead of relying on `recordRepresentation`. + +```jsx +import { AutocompleteArrayInput, ReferenceArrayInput } from 'react-admin'; -}/> + + + ``` ## `optionValue` @@ -517,6 +542,7 @@ const OptionRenderer = () => { ); }; +const optionText = ; const inputText = choice => `${choice.first_name} ${choice.last_name}`; const matchSuggestion = (filter, choice) => { return ( @@ -528,12 +554,30 @@ const matchSuggestion = (filter, choice) => { } + optionText={optionText} inputText={inputText} matchSuggestion={matchSuggestion} /> ``` +**Tip**: Make sure you pass stable references to the functions passed to the `inputText` and `matchSuggestion` by either declaring them outside the component render function or by wrapping them in a [`useCallback`](https://react.dev/reference/react/useCallback). + +**Tip**: Make sure you pass a stable reference to the element passed to the `optionText` prop by calling it outside the component render function like so: + +```jsx +const OptionRenderer = () => { + const record = useRecordContext(); + return ( + + + {record.first_name} {record.last_name} + + ); +}; + +const optionText = ; +``` + ## Creating New Choices The `` can allow users to create a new choice if either the `create` or `onCreate` prop is provided. diff --git a/docs/AutocompleteInput.md b/docs/AutocompleteInput.md index d48ddc650ed..49cfd6bafa4 100644 --- a/docs/AutocompleteInput.md +++ b/docs/AutocompleteInput.md @@ -370,10 +370,15 @@ const choices = [ { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, { id: 456, first_name: 'Jane', last_name: 'Austen' }, ]; + +// Note we declared the function outside the component to avoid rerenders const optionRenderer = choice => `${choice.first_name} ${choice.last_name}`; + ``` +**Tip**: Make sure you provide a stable reference to the function passed as `optionText`. Either declare it outside the component render function or wrap it inside a [`useCallback`](https://react.dev/reference/react/useCallback). + `optionText` also accepts a React Element, that will be rendered inside a [``](./useRecordContext.md) using the related choice as the `record` prop. You can use Field components there. However, using an element as `optionText` implies that you also set two more props, `inputText` and `matchSuggestion`. See [Using A Custom Element For Options](#using-a-custom-element-for-options) for more details. `optionText` is also useful when the choices are records [fetched from another resource](#fetching-choices), and `` is a child of a [``](./ReferenceInput.md). @@ -637,6 +642,8 @@ const OptionRenderer = () => { ); }; + +const optionText = ; const inputText = choice => `${choice.first_name} ${choice.last_name}`; const matchSuggestion = (filter, choice) => { return ( @@ -648,12 +655,30 @@ const matchSuggestion = (filter, choice) => { } + optionText={optionText} inputText={inputText} matchSuggestion={matchSuggestion} /> ``` +**Tip**: Make sure you pass stable references to the functions passed to the `inputText` and `matchSuggestion` by either declaring them outside the component render function or by wrapping them in a [`useCallback`](https://react.dev/reference/react/useCallback). + +**Tip**: Make sure you pass a stable reference to the element passed to the `optionText` prop by calling it outside the component render function like so: + +```jsx +const OptionRenderer = () => { + const record = useRecordContext(); + return ( + + + {record.first_name} {record.last_name} + + ); +}; + +const optionText = ; +``` + ## Creating New Choices The `` can allow users to create a new choice if either the `create` or `onCreate` prop is provided. diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index 623315f9a7a..7496f2d8522 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -33,6 +33,7 @@ import { useTranslate, warning, useGetRecordRepresentation, + useEvent, } from 'ra-core'; import { SupportCreateSuggestionOptions, @@ -151,7 +152,7 @@ export const AutocompleteInput = < matchSuggestion, margin, fieldState: fieldStateOverride, - filterToQuery = DefaultFilterToQuery, + filterToQuery: filterToQueryProp = DefaultFilterToQuery, formState: formStateOverride, multiple = false, noOptionsText, @@ -175,6 +176,8 @@ export const AutocompleteInput = < ...rest } = props; + const filterToQuery = useEvent(filterToQueryProp); + const { allChoices, isLoading,