Skip to content

Commit 51b93be

Browse files
committed
Improve API
1 parent ecaa065 commit 51b93be

File tree

4 files changed

+65
-51
lines changed

4 files changed

+65
-51
lines changed

packages/ra-core/src/controller/input/SimpleFormIteratorBase.tsx

Lines changed: 7 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import * as React from 'react';
2-
import { Children, type ReactNode, useMemo, useRef } from 'react';
2+
import { type ReactNode, useMemo } from 'react';
33
import { type UseFieldArrayReturn, useFormContext } from 'react-hook-form';
4-
import { FormDataConsumer } from '../../form/FormDataConsumer';
54
import { useWrappedSource } from '../../core/useWrappedSource';
65
import type { RaRecord } from '../../types';
76
import { useEvent } from '../../util';
87
import { useArrayInput } from './useArrayInput';
98
import { SimpleFormIteratorContext } from './SimpleFormIteratorContext';
109

10+
const DefaultOnAddItem = item => item;
11+
1112
export const SimpleFormIteratorBase = (props: SimpleFormIteratorBaseProps) => {
12-
const { children, inputs } = props;
13+
const { children, onAddItem: onAddItemProp = DefaultOnAddItem } = props;
14+
const onAddItem = useEvent(onAddItemProp);
1315

1416
const finalSource = useWrappedSource('');
1517
if (!finalSource) {
@@ -20,7 +22,6 @@ export const SimpleFormIteratorBase = (props: SimpleFormIteratorBaseProps) => {
2022

2123
const { append, fields, move, remove, replace } = useArrayInput(props);
2224
const { trigger, getValues } = useFormContext();
23-
const initialDefaultValue = useRef({});
2425

2526
const removeField = useEvent((index: number) => {
2627
remove(index);
@@ -34,46 +35,8 @@ export const SimpleFormIteratorBase = (props: SimpleFormIteratorBaseProps) => {
3435
}
3536
});
3637

37-
if (fields.length > 0) {
38-
const { id, ...rest } = fields[0];
39-
initialDefaultValue.current = rest;
40-
for (const k in initialDefaultValue.current)
41-
initialDefaultValue.current[k] = null;
42-
}
43-
4438
const addField = useEvent((item: any = undefined) => {
45-
let defaultValue = item;
46-
if (item == null) {
47-
defaultValue = initialDefaultValue.current;
48-
if (
49-
Children.count(inputs) === 1 &&
50-
React.isValidElement(Children.only(inputs)) &&
51-
// @ts-ignore
52-
!Children.only(inputs).props.source &&
53-
// Make sure it's not a FormDataConsumer
54-
// @ts-ignore
55-
Children.only(inputs).type !== FormDataConsumer
56-
) {
57-
// ArrayInput used for an array of scalar values
58-
// (e.g. tags: ['foo', 'bar'])
59-
defaultValue = '';
60-
} else {
61-
// ArrayInput used for an array of objects
62-
// (e.g. authors: [{ firstName: 'John', lastName: 'Doe' }, { firstName: 'Jane', lastName: 'Doe' }])
63-
defaultValue = defaultValue || ({} as Record<string, unknown>);
64-
Children.forEach(inputs, input => {
65-
if (
66-
React.isValidElement(input) &&
67-
input.type !== FormDataConsumer &&
68-
input.props.source
69-
) {
70-
defaultValue[input.props.source] =
71-
input.props.defaultValue ?? null;
72-
}
73-
});
74-
}
75-
}
76-
append(defaultValue);
39+
append(onAddItem(item));
7740
});
7841

7942
const handleReorder = useEvent((origin: number, destination: number) => {
@@ -117,12 +80,12 @@ export interface SimpleFormIteratorBaseProps
11780
extends Partial<UseFieldArrayReturn> {
11881
children: ReactNode;
11982
inline?: boolean;
120-
inputs: ReactNode;
12183
meta?: {
12284
// the type defined in FieldArrayRenderProps says error is boolean, which is wrong.
12385
error?: any;
12486
submitFailed?: boolean;
12587
};
88+
onAddItem?: (item: any) => any;
12689
record?: RaRecord;
12790
resource?: string;
12891
source?: string;

packages/ra-core/src/controller/input/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ export * from './SimpleFormIteratorContext';
1313
export * from './SimpleFormIteratorItemContext';
1414
export * from './useSimpleFormIterator';
1515
export * from './useSimpleFormIteratorItem';
16+
export * from './useGetArrayInputNewItemDefaults';
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { Children, isValidElement, useRef, type ReactNode } from 'react';
2+
import { FormDataConsumer } from '../../form/FormDataConsumer';
3+
import type { ArrayInputContextValue } from './ArrayInputContext';
4+
import { useEvent } from '../../util';
5+
6+
export const useGetArrayInputNewItemDefaults = (
7+
fields: ArrayInputContextValue['fields']
8+
) => {
9+
const initialDefaultValue = useRef<Record<string, unknown>>({});
10+
if (fields.length > 0) {
11+
const { id, ...rest } = fields[0];
12+
initialDefaultValue.current = rest;
13+
for (const k in initialDefaultValue.current)
14+
initialDefaultValue.current[k] = null;
15+
}
16+
17+
return useEvent((inputs?: ReactNode) => {
18+
if (
19+
Children.count(inputs) === 1 &&
20+
isValidElement(Children.only(inputs)) &&
21+
// @ts-ignore
22+
!Children.only(inputs).props.source &&
23+
// Make sure it's not a FormDataConsumer
24+
// @ts-ignore
25+
Children.only(inputs).type !== FormDataConsumer
26+
) {
27+
// ArrayInput used for an array of scalar values
28+
// (e.g. tags: ['foo', 'bar'])
29+
return '';
30+
}
31+
32+
// ArrayInput used for an array of objects
33+
// (e.g. authors: [{ firstName: 'John', lastName: 'Doe' }, { firstName: 'Jane', lastName: 'Doe' }])
34+
const defaultValue = initialDefaultValue.current;
35+
Children.forEach(inputs, input => {
36+
if (
37+
isValidElement(input) &&
38+
input.type !== FormDataConsumer &&
39+
input.props.source
40+
) {
41+
defaultValue[input.props.source] =
42+
input.props.defaultValue ?? null;
43+
}
44+
});
45+
return defaultValue;
46+
});
47+
};

packages/ra-ui-materialui/src/input/ArrayInput/SimpleFormIterator.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import {
1717
RecordContextProvider,
1818
useArrayInput,
1919
useRecordContext,
20+
useEvent,
21+
useGetArrayInputNewItemDefaults,
2022
} from 'ra-core';
2123
import get from 'lodash/get';
2224

@@ -62,15 +64,16 @@ export const SimpleFormIterator = (inProps: SimpleFormIteratorProps) => {
6264
const { fields } = useArrayInput(props);
6365
const record = useRecordContext(props);
6466
const records = get(record, finalSource);
67+
const getArrayInputNewItemDefaults =
68+
useGetArrayInputNewItemDefaults(fields);
69+
70+
const handleAddItem = useEvent((item: any = undefined) => {
71+
if (item != null) return item;
72+
return getArrayInputNewItemDefaults(children);
73+
});
6574

6675
return (
67-
<SimpleFormIteratorBase
68-
// SimpleFormIteratorBase needs to know the actual inputs to correctly handle new items
69-
// (single item without source, or multiple items, FormDataConsumer...)
70-
// See its addField function
71-
inputs={children}
72-
{...props}
73-
>
76+
<SimpleFormIteratorBase {...props} onAddItem={handleAddItem}>
7477
<Root
7578
className={clsx(
7679
className,

0 commit comments

Comments
 (0)