Skip to content

Commit df82203

Browse files
authored
fix(form): support single child wrapped in array (#7075)
Co-authored-by: casds-FDXS <你的 GitHub 邮箱>
1 parent 2529078 commit df82203

2 files changed

Lines changed: 52 additions & 11 deletions

File tree

src/components/form/form-item.tsx

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ const NAME_SPLIT = '__SPLIT__'
2222
type RenderChildren<Values = any> = (form: FormInstance<Values>) => ReactNode
2323
type ChildrenType<Values = any> = RenderChildren<Values> | ReactNode
2424

25+
function useChildren(children?: ChildrenType): ChildrenType {
26+
if (typeof children === 'function') {
27+
return children
28+
}
29+
30+
const childList = toArray(children)
31+
return childList.length <= 1 ? childList[0] : childList
32+
}
33+
2534
type RcFieldProps = Omit<FieldProps, 'children'>
2635

2736
const classPrefix = `adm-form-item`
@@ -265,6 +274,8 @@ export const FormItem: FC<FormItemProps> = props => {
265274
...fieldProps
266275
} = props
267276

277+
const mergedChildren = useChildren(children)
278+
268279
const { name: formName } = useContext(FormContext)
269280
const { validateTrigger: contextValidateTrigger } = useContext(FieldContext)
270281

@@ -359,10 +370,10 @@ export const FormItem: FC<FormItemProps> = props => {
359370
)
360371
}
361372

362-
const isRenderProps = typeof children === 'function'
373+
const isRenderProps = typeof mergedChildren === 'function'
363374

364375
if (!name && !isRenderProps && !props.dependencies) {
365-
return renderLayout(children) as JSX.Element
376+
return renderLayout(mergedChildren) as JSX.Element
366377
}
367378

368379
let Variables: Record<string, string> = {}
@@ -416,7 +427,7 @@ export const FormItem: FC<FormItemProps> = props => {
416427

417428
if (isRenderProps) {
418429
if ((shouldUpdate || dependencies) && !name) {
419-
childNode = (children as RenderChildren)(context)
430+
childNode = (mergedChildren as RenderChildren)(context)
420431
} else {
421432
if (!(shouldUpdate || dependencies)) {
422433
devWarning(
@@ -438,18 +449,18 @@ export const FormItem: FC<FormItemProps> = props => {
438449
'Form.Item',
439450
'Must set `name` or use render props when `dependencies` is set.'
440451
)
441-
} else if (React.isValidElement(children)) {
442-
if (children.props.defaultValue) {
452+
} else if (React.isValidElement(mergedChildren)) {
453+
if (mergedChildren.props.defaultValue) {
443454
devWarning(
444455
'Form.Item',
445456
'`defaultValue` will not work on controlled Field. You should use `initialValues` of Form instead.'
446457
)
447458
}
448-
const childProps = { ...children.props, ...control }
459+
const childProps = { ...mergedChildren.props, ...control }
449460

450-
if (isSafeSetRefComponent(children)) {
461+
if (isSafeSetRefComponent(mergedChildren)) {
451462
childProps.ref = (instance: any) => {
452-
const originRef = (children as any).ref
463+
const originRef = (mergedChildren as any).ref
453464
if (originRef) {
454465
if (typeof originRef === 'function') {
455466
originRef(instance)
@@ -475,7 +486,7 @@ export const FormItem: FC<FormItemProps> = props => {
475486
triggers.forEach(eventName => {
476487
childProps[eventName] = (...args: any[]) => {
477488
control[eventName]?.(...args)
478-
children.props[eventName]?.(...args)
489+
mergedChildren.props[eventName]?.(...args)
479490
}
480491
})
481492

@@ -484,7 +495,7 @@ export const FormItem: FC<FormItemProps> = props => {
484495
value={control[props.valuePropName || 'value']}
485496
update={updateRef.current}
486497
>
487-
{React.cloneElement(children, childProps)}
498+
{React.cloneElement(mergedChildren, childProps)}
488499
</MemoInput>
489500
)
490501
} else {
@@ -494,7 +505,7 @@ export const FormItem: FC<FormItemProps> = props => {
494505
'`name` is only used for validate React element. If you are using Form.Item as layout display, please remove `name` instead.'
495506
)
496507
}
497-
childNode = children
508+
childNode = mergedChildren
498509
}
499510

500511
return renderLayout(childNode, fieldId, meta, isRequired)

src/components/form/tests/form.test.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,36 @@ describe('Form', () => {
343343
})
344344

345345
describe('Form.Item', () => {
346+
test('supports a single child wrapped in an array', async () => {
347+
const onFinish = jest.fn()
348+
const { container, getByText } = render(
349+
<Form
350+
initialValues={{ name: 'bamboo' }}
351+
onFinish={onFinish}
352+
footer={
353+
<Button block type='submit'>
354+
submit
355+
</Button>
356+
}
357+
>
358+
<Form.Item name='name' label='Name'>
359+
{[<Input key='input' />]}
360+
</Form.Item>
361+
</Form>
362+
)
363+
364+
const input = container.querySelector('input') as HTMLInputElement
365+
expect(input.value).toBe('bamboo')
366+
367+
fireEvent.change(input, { target: { value: 'little' } })
368+
fireEvent.click(getByText('submit'))
369+
370+
await waitFor(() => {
371+
expect(onFinish).toHaveBeenCalled()
372+
})
373+
expect(onFinish.mock.calls[0][0]).toEqual({ name: 'little' })
374+
})
375+
346376
test('noStyle', async () => {
347377
const onChange = jest.fn()
348378
const { container } = render(

0 commit comments

Comments
 (0)