From 6a07cb322384d46195ba8b1294f512f0f1a7ba0b Mon Sep 17 00:00:00 2001 From: aayush Date: Sun, 16 Aug 2026 11:42:10 +0530 Subject: [PATCH 1/4] feat: expose index in FormDataConsumer --- .../src/form/FormDataConsumer.spec.tsx | 33 +++++++++++++++++++ .../ra-core/src/form/FormDataConsumer.tsx | 4 ++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/packages/ra-core/src/form/FormDataConsumer.spec.tsx b/packages/ra-core/src/form/FormDataConsumer.spec.tsx index 6498b370990..2fb18f2220b 100644 --- a/packages/ra-core/src/form/FormDataConsumer.spec.tsx +++ b/packages/ra-core/src/form/FormDataConsumer.spec.tsx @@ -140,4 +140,37 @@ describe('FormDataConsumerView', () => { }); }); }); + + it('calls its children with the index when inside an ArrayInput', async () => { + let globalIndex; + + render( + + + + + + + {({ index }) => { + globalIndex = index; + return null; + }} + + + + + + + ); + + expect(globalIndex).toEqual(undefined); + + fireEvent.click(screen.getByLabelText('ra.action.add')); + + expect(globalIndex).toEqual(0); + + fireEvent.click(screen.getByLabelText('ra.action.add')); + + expect(globalIndex).toEqual(1); + }); }); diff --git a/packages/ra-core/src/form/FormDataConsumer.tsx b/packages/ra-core/src/form/FormDataConsumer.tsx index f0f4176ec1e..b280210367b 100644 --- a/packages/ra-core/src/form/FormDataConsumer.tsx +++ b/packages/ra-core/src/form/FormDataConsumer.tsx @@ -77,7 +77,8 @@ export const FormDataConsumerView = < // If we have an index, we are in an iterator like component (such as the SimpleFormIterator) if (arraySource) { const scopedFormData = get(formData, arraySource); - result = children({ formData, scopedFormData }); + const index = Number(arraySource.match(/\d+$/)?.[0]); + result = children({ formData, scopedFormData, index }); } else { result = children({ formData }); } @@ -98,6 +99,7 @@ export interface FormDataConsumerRenderParams< > { formData: TFieldValues; scopedFormData?: TScopedFieldValues; + index?: number; } export type FormDataConsumerRender< From 582a7424da35e8b85b3eb329679f66bd5844a0dd Mon Sep 17 00:00:00 2001 From: Aayush5154 Date: Tue, 25 Aug 2026 15:13:37 +0530 Subject: [PATCH 2/4] done with the issue --- .../src/form/FormDataConsumer.spec.tsx | 41 +++++++++++++++++++ .../ra-core/src/form/FormDataConsumer.tsx | 3 +- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/packages/ra-core/src/form/FormDataConsumer.spec.tsx b/packages/ra-core/src/form/FormDataConsumer.spec.tsx index 2fb18f2220b..a45fc7acc88 100644 --- a/packages/ra-core/src/form/FormDataConsumer.spec.tsx +++ b/packages/ra-core/src/form/FormDataConsumer.spec.tsx @@ -173,4 +173,45 @@ describe('FormDataConsumerView', () => { expect(globalIndex).toEqual(1); }); + + it('calls its children with the correct index when inside nested ArrayInputs', async () => { + let innerIndex: number | undefined; + + render( + + + + + + + + + {({ index }) => { + innerIndex = index; + return null; + }} + + + + + + + + + ); + + await waitFor(() => { + // The inner array's first item should have index 0, + // not the outer array's index (also 0 in this case) + expect(innerIndex).toEqual(0); + }); + }); }); diff --git a/packages/ra-core/src/form/FormDataConsumer.tsx b/packages/ra-core/src/form/FormDataConsumer.tsx index b280210367b..215d0da6616 100644 --- a/packages/ra-core/src/form/FormDataConsumer.tsx +++ b/packages/ra-core/src/form/FormDataConsumer.tsx @@ -77,7 +77,8 @@ export const FormDataConsumerView = < // If we have an index, we are in an iterator like component (such as the SimpleFormIterator) if (arraySource) { const scopedFormData = get(formData, arraySource); - const index = Number(arraySource.match(/\d+$/)?.[0]); + const matches = [...arraySource.matchAll(/\d+/g)]; + const index = Number(matches[matches.length - 1]?.[0]); result = children({ formData, scopedFormData, index }); } else { result = children({ formData }); From e09352ae691557403264e7bf218f374045039e69 Mon Sep 17 00:00:00 2001 From: Aayush5154 Date: Wed, 2 Sep 2026 17:00:09 +0530 Subject: [PATCH 3/4] docs: document index parameter in FormDataConsumer --- docs/Forms.md | 5 +++-- docs/Inputs.md | 5 +++-- docs/SimpleFormIterator.md | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/Forms.md b/docs/Forms.md index cf224c23d2e..4c04a4381d7 100644 --- a/docs/Forms.md +++ b/docs/Forms.md @@ -314,7 +314,7 @@ const OrderEdit = () => ( ); ``` -**Tip**: When used inside an `ArrayInput`, `` provides one additional property to its child function called `scopedFormData`. It's an object containing the current values of the *currently rendered item*. This allows you to create dependencies between inputs inside a ``, as in the following example: +**Tip**: When used inside an ``, `` provides two additional properties to its child function: `scopedFormData` (an object containing the current values of the *currently rendered item*) and `index` (the zero-based index of the item inside the array). This allows you to create dependencies between inputs inside a ``, as in the following example: ```tsx import { FormDataConsumer } from 'react-admin'; @@ -329,6 +329,7 @@ const PostEdit = () => ( {({ formData, // The whole form data scopedFormData, // The data for this item of the ArrayInput + index, // The index of this item of the ArrayInput ...rest }) => scopedFormData && scopedFormData.name ? ( @@ -347,7 +348,7 @@ const PostEdit = () => ( ); ``` -**Tip:** TypeScript users will notice that `scopedFormData` is typed as an optional parameter. This is because the `` component can be used outside of an `` and in that case, this parameter will be `undefined`. If you are inside an ``, you can safely assume that this parameter will be defined. +**Tip:** TypeScript users will notice that `scopedFormData` and `index` are typed as optional parameters. This is because the `` component can be used outside of an `` and in that case, these parameters will be `undefined`. If you are inside an ``, you can safely assume that these parameters will be defined. ## Hiding Inputs Based On Other Inputs diff --git a/docs/Inputs.md b/docs/Inputs.md index f5b92675856..e2870672295 100644 --- a/docs/Inputs.md +++ b/docs/Inputs.md @@ -673,7 +673,7 @@ const OrderEdit = () => ( ); ``` -**Tip**: When used inside an ``, `` provides one additional property to its child function called `scopedFormData`. It's an object containing the current values of the *currently rendered item*. This allows you to create dependencies between inputs inside a ``, as in the following example: +**Tip**: When used inside an ``, `` provides two additional properties to its child function: `scopedFormData` (an object containing the current values of the *currently rendered item*) and `index` (the zero-based index of the item inside the array). This allows you to create dependencies between inputs inside a ``, as in the following example: ```tsx import { FormDataConsumer } from 'react-admin'; @@ -688,6 +688,7 @@ const PostEdit = () => ( {({ formData, // The whole form data scopedFormData, // The data for this item of the ArrayInput + index, // The index of this item of the ArrayInput ...rest }) => scopedFormData && scopedFormData.name ? ( @@ -706,7 +707,7 @@ const PostEdit = () => ( ); ``` -**Tip:** TypeScript users will notice that `scopedFormData` is typed as an optional parameter. This is because the `` component can be used outside of an `` and in that case, this parameter will be `undefined`. If you are inside an ``, you can safely assume that this parameter will be defined. +**Tip:** TypeScript users will notice that `scopedFormData` and `index` are typed as optional parameters. This is because the `` component can be used outside of an `` and in that case, these parameters will be `undefined`. If you are inside an ``, you can safely assume that these parameters will be defined. **Tip:** If you need to access the *effective* source of an input inside an ``, for example to change the value programmatically using `setValue`, you will need to leverage the [`useSourceContext` hook](./ArrayInput#changing-an-items-value-programmatically). diff --git a/docs/SimpleFormIterator.md b/docs/SimpleFormIterator.md index 44d3b0762b9..f0de2612375 100644 --- a/docs/SimpleFormIterator.md +++ b/docs/SimpleFormIterator.md @@ -135,7 +135,7 @@ A list of Input elements, that will be rendered on each row. By default, `` renders one input per line, but they can be displayed inline with the `inline` prop. -`` also accepts `` as child. In this case, `` provides one additional property to its child function called `scopedFormData`. It's an object containing the current values of the *currently rendered item*. This allows you to create dependencies between inputs inside a ``, as in the following example: +`` also accepts `` as child. In this case, `` provides two additional properties to its child function: `scopedFormData` (an object containing the current values of the *currently rendered item*) and `index` (the zero-based index of the item inside the array). This allows you to create dependencies between inputs inside a ``, as in the following example: ```jsx import { FormDataConsumer } from 'react-admin'; @@ -150,6 +150,7 @@ const PostEdit = () => ( {({ formData, // The whole form data scopedFormData, // The data for this item of the ArrayInput + index, // The index of this item of the ArrayInput }) => scopedFormData && scopedFormData.name ? ( ( ); ``` -**Tip:** TypeScript users will notice that `scopedFormData` is typed as an optional parameter. This is because the `` component can be used outside of an `` and in that case, this parameter will be `undefined`. If you are inside an ``, you can safely assume that this parameter will be defined. +**Tip:** TypeScript users will notice that `scopedFormData` and `index` are typed as optional parameters. This is because the `` component can be used outside of an `` and in that case, these parameters will be `undefined`. If you are inside an ``, you can safely assume that these parameters will be defined. **Note**: `` only accepts `Input` components as children. If you want to use some `Fields` instead, you have to use a ``, as follows: From ee6aeee7d1d44e0f8b730e5946e49d01ffc75699 Mon Sep 17 00:00:00 2001 From: Aayush5154 Date: Wed, 2 Sep 2026 17:07:02 +0530 Subject: [PATCH 4/4] docs: document index parameter in FormDataConsumer --- docs/Forms.md | 6 ++++-- docs/Inputs.md | 6 ++++-- docs/SimpleFormIterator.md | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/Forms.md b/docs/Forms.md index 4c04a4381d7..e65536ed77a 100644 --- a/docs/Forms.md +++ b/docs/Forms.md @@ -314,7 +314,7 @@ const OrderEdit = () => ( ); ``` -**Tip**: When used inside an ``, `` provides two additional properties to its child function: `scopedFormData` (an object containing the current values of the *currently rendered item*) and `index` (the zero-based index of the item inside the array). This allows you to create dependencies between inputs inside a ``, as in the following example: +**Tip**: When used inside an `ArrayInput`, `` provides two additional properties to its child function: `scopedFormData` and `index`. `scopedFormData` is an object containing the current values of the *currently rendered item*. `index` is the index of the current item in the array. This allows you to create dependencies between inputs inside a ``, as in the following example: ```tsx import { FormDataConsumer } from 'react-admin'; @@ -329,7 +329,7 @@ const PostEdit = () => ( {({ formData, // The whole form data scopedFormData, // The data for this item of the ArrayInput - index, // The index of this item of the ArrayInput + index, // The index of the current item in the ArrayInput ...rest }) => scopedFormData && scopedFormData.name ? ( @@ -350,6 +350,8 @@ const PostEdit = () => ( **Tip:** TypeScript users will notice that `scopedFormData` and `index` are typed as optional parameters. This is because the `` component can be used outside of an `` and in that case, these parameters will be `undefined`. If you are inside an ``, you can safely assume that these parameters will be defined. +**Tip:** The `index` parameter is useful when you need to know the position of the current item in the array, e.g. to display a row number or to apply conditional logic based on the item's position. + ## Hiding Inputs Based On Other Inputs You may want to display or hide inputs based on the value of another input - for instance, show an `email` input only if the `hasEmail` boolean input has been ticked to `true`. diff --git a/docs/Inputs.md b/docs/Inputs.md index e2870672295..871a57e0939 100644 --- a/docs/Inputs.md +++ b/docs/Inputs.md @@ -673,7 +673,7 @@ const OrderEdit = () => ( ); ``` -**Tip**: When used inside an ``, `` provides two additional properties to its child function: `scopedFormData` (an object containing the current values of the *currently rendered item*) and `index` (the zero-based index of the item inside the array). This allows you to create dependencies between inputs inside a ``, as in the following example: +**Tip**: When used inside an ``, `` provides two additional properties to its child function: `scopedFormData` and `index`. `scopedFormData` is an object containing the current values of the *currently rendered item*. `index` is the index of the current item in the array. This allows you to create dependencies between inputs inside a ``, as in the following example: ```tsx import { FormDataConsumer } from 'react-admin'; @@ -688,7 +688,7 @@ const PostEdit = () => ( {({ formData, // The whole form data scopedFormData, // The data for this item of the ArrayInput - index, // The index of this item of the ArrayInput + index, // The index of the current item in the ArrayInput ...rest }) => scopedFormData && scopedFormData.name ? ( @@ -709,6 +709,8 @@ const PostEdit = () => ( **Tip:** TypeScript users will notice that `scopedFormData` and `index` are typed as optional parameters. This is because the `` component can be used outside of an `` and in that case, these parameters will be `undefined`. If you are inside an ``, you can safely assume that these parameters will be defined. +**Tip:** The `index` parameter is useful when you need to know the position of the current item in the array, e.g. to display a row number or to apply conditional logic based on the item's position. + **Tip:** If you need to access the *effective* source of an input inside an ``, for example to change the value programmatically using `setValue`, you will need to leverage the [`useSourceContext` hook](./ArrayInput#changing-an-items-value-programmatically). ## Hiding Inputs Based On Other Inputs diff --git a/docs/SimpleFormIterator.md b/docs/SimpleFormIterator.md index f0de2612375..ba2b389e7ce 100644 --- a/docs/SimpleFormIterator.md +++ b/docs/SimpleFormIterator.md @@ -135,7 +135,7 @@ A list of Input elements, that will be rendered on each row. By default, `` renders one input per line, but they can be displayed inline with the `inline` prop. -`` also accepts `` as child. In this case, `` provides two additional properties to its child function: `scopedFormData` (an object containing the current values of the *currently rendered item*) and `index` (the zero-based index of the item inside the array). This allows you to create dependencies between inputs inside a ``, as in the following example: +`` also accepts `` as child. In this case, `` provides two additional properties to its child function: `scopedFormData` and `index`. `scopedFormData` is an object containing the current values of the *currently rendered item*. `index` is the index of the current item in the array. This allows you to create dependencies between inputs inside a ``, as in the following example: ```jsx import { FormDataConsumer } from 'react-admin'; @@ -150,7 +150,7 @@ const PostEdit = () => ( {({ formData, // The whole form data scopedFormData, // The data for this item of the ArrayInput - index, // The index of this item of the ArrayInput + index, // The index of the current item in the ArrayInput }) => scopedFormData && scopedFormData.name ? ( ( **Tip:** TypeScript users will notice that `scopedFormData` and `index` are typed as optional parameters. This is because the `` component can be used outside of an `` and in that case, these parameters will be `undefined`. If you are inside an ``, you can safely assume that these parameters will be defined. +**Tip:** The `index` parameter is useful when you need to know the position of the current item in the array, e.g. to display a row number or to apply conditional logic based on the item's position. + **Note**: `` only accepts `Input` components as children. If you want to use some `Fields` instead, you have to use a ``, as follows: ```jsx