diff --git a/packages/ra-core/src/form/FormDataConsumer.tsx b/packages/ra-core/src/form/FormDataConsumer.tsx index 39205f7ec2..7659835765 100644 --- a/packages/ra-core/src/form/FormDataConsumer.tsx +++ b/packages/ra-core/src/form/FormDataConsumer.tsx @@ -4,6 +4,7 @@ import { useFormContext, FieldValues } from 'react-hook-form'; import get from 'lodash/get'; import { useFormValues } from './useFormValues'; import { useWrappedSource } from '../core'; +import { useEvent } from '../util'; /** * Get the current (edited) value of the record from the form and pass it @@ -67,22 +68,26 @@ export const FormDataConsumerView = < props: Props ) => { const { children, formData, source } = props; - let ret; + const [result, setResult] = React.useState(null); const finalSource = useWrappedSource(source || ''); + const render = useEvent(children); - // Passes an empty string here as we don't have the children sources and we just want to know if we are in an iterator - const matches = ArraySourceRegex.exec(finalSource); + // Getting the result of the children function in a useEffect allows us to keep a stable reference to is + // with useEvent + React.useEffect(() => { + // Passes an empty string here as we don't have the children sources and we just want to know if we are in an iterator + const matches = ArraySourceRegex.exec(finalSource); + // If we have an index, we are in an iterator like component (such as the SimpleFormIterator) + if (matches) { + const scopedFormData = get(formData, matches[0]); + setResult(render({ formData, scopedFormData })); + } else { + setResult(render({ formData })); + } + }, [finalSource, formData, render]); - // If we have an index, we are in an iterator like component (such as the SimpleFormIterator) - if (matches) { - const scopedFormData = get(formData, matches[0]); - ret = children({ formData, scopedFormData }); - } else { - ret = children({ formData }); - } - - return ret === undefined ? null : ret; + return result; }; const ArraySourceRegex = new RegExp(/.+\.\d+$/); diff --git a/packages/ra-ui-materialui/src/form/FormDataConsumer.stories.tsx b/packages/ra-ui-materialui/src/form/FormDataConsumer.stories.tsx new file mode 100644 index 0000000000..e05cd50afe --- /dev/null +++ b/packages/ra-ui-materialui/src/form/FormDataConsumer.stories.tsx @@ -0,0 +1,95 @@ +import * as React from 'react'; +import { FormDataConsumer, required, ResourceContextProvider } from 'ra-core'; +import fakeRestDataProvider from 'ra-data-fakerest'; +import { AdminContext } from '../AdminContext'; +import { AutocompleteInput, ReferenceInput, TextInput } from '../input'; +import { SimpleForm } from './SimpleForm'; +import { Create } from '../detail'; + +// We keep this test in ra-ui-materialui because we need heavy components to reproduce the issue https://github.com/marmelab/react-admin/issues/10415 +export default { title: 'ra-core/form/FormDataConsumer' }; + +export const Basic = () => ( + + + + + + > + {({ formData }) => { + console.log({ formData }); + if (!formData.title) { + return null; + } + return ( + + + `${choice.name} / (${choice.id})` + } + noOptionsText="User doesn't exist" + isRequired + validate={[ + required('User is required.'), + ]} + /> + + ); + }} + + + + + + +); + +const dataProvider = fakeRestDataProvider({ + users: [ + { + id: 1, + name: 'Leanne Graham', + }, + { + id: 2, + name: 'Ervin Howell', + }, + { + id: 3, + name: 'Clementine Bauch', + }, + { + id: 4, + name: 'Patricia Lebsack', + }, + { + id: 5, + name: 'Chelsey Dietrich', + }, + { + id: 6, + name: 'Mrs. Dennis Schulist', + }, + { + id: 7, + name: 'Kurtis Weissnat', + }, + { + id: 8, + name: 'Nicholas Runolfsdottir V', + }, + { + id: 9, + name: 'Glenna Reichert', + }, + { + id: 10, + name: 'Clementina DuBuque', + }, + ], +});