Browse Source
refactor: Form.Item with pure children will only render once (#21991)
* refactor: Form.Item with pure children will only render once
* not skip for real render
pull/21993/head
二货机器人
5 years ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with
58 additions and
1 deletions
-
components/form/FormItem.tsx
-
components/form/__tests__/index.test.js
|
|
@ -21,6 +21,21 @@ type RenderChildren = (form: FormInstance) => React.ReactNode; |
|
|
|
type RcFieldProps = Omit<FieldProps, 'children'>; |
|
|
|
type ChildrenType = React.ReactElement | RenderChildren | React.ReactElement[] | null; |
|
|
|
|
|
|
|
interface MemoInputProps { |
|
|
|
value: any; |
|
|
|
update: number; |
|
|
|
children: any; |
|
|
|
} |
|
|
|
|
|
|
|
const MemoInput = React.memo<MemoInputProps>( |
|
|
|
({ children }) => { |
|
|
|
return children; |
|
|
|
}, |
|
|
|
(prev, next) => { |
|
|
|
return prev.value === next.value && prev.update === next.update; |
|
|
|
}, |
|
|
|
); |
|
|
|
|
|
|
|
export interface FormItemProps |
|
|
|
extends FormItemLabelProps, |
|
|
|
FormItemInputProps, |
|
|
@ -216,6 +231,10 @@ function FormItem(props: FormItemProps): React.ReactElement { |
|
|
|
return renderLayout(children); |
|
|
|
} |
|
|
|
|
|
|
|
// Record for real component render
|
|
|
|
const updateRef = React.useRef(0); |
|
|
|
updateRef.current += 1; |
|
|
|
|
|
|
|
return ( |
|
|
|
<Field |
|
|
|
{...props} |
|
|
@ -296,7 +315,14 @@ function FormItem(props: FormItemProps): React.ReactElement { |
|
|
|
}; |
|
|
|
}); |
|
|
|
|
|
|
|
childNode = React.cloneElement(children, childProps); |
|
|
|
childNode = ( |
|
|
|
<MemoInput |
|
|
|
value={mergedControl[props.valuePropName || 'value']} |
|
|
|
update={updateRef.current} |
|
|
|
> |
|
|
|
{React.cloneElement(children, childProps)} |
|
|
|
</MemoInput> |
|
|
|
); |
|
|
|
} else if (isRenderProps && shouldUpdate && !hasName) { |
|
|
|
childNode = (children as RenderChildren)(context); |
|
|
|
} else { |
|
|
|
|
|
@ -621,4 +621,35 @@ describe('Form', () => { |
|
|
|
} |
|
|
|
expect(instances.size).toEqual(1); |
|
|
|
}); |
|
|
|
|
|
|
|
it('avoid re-render', async () => { |
|
|
|
let renderTimes = 0; |
|
|
|
|
|
|
|
const MyInput = ({ value = '', ...props }) => { |
|
|
|
renderTimes += 1; |
|
|
|
return <input value={value} {...props} />; |
|
|
|
}; |
|
|
|
|
|
|
|
const Demo = () => ( |
|
|
|
<Form> |
|
|
|
<Form.Item name="username" rules={[{ required: true }]}> |
|
|
|
<MyInput /> |
|
|
|
</Form.Item> |
|
|
|
</Form> |
|
|
|
); |
|
|
|
|
|
|
|
const wrapper = mount(<Demo />); |
|
|
|
renderTimes = 0; |
|
|
|
|
|
|
|
wrapper.find('input').simulate('change', { |
|
|
|
target: { |
|
|
|
value: 'a', |
|
|
|
}, |
|
|
|
}); |
|
|
|
|
|
|
|
await delay(); |
|
|
|
|
|
|
|
expect(renderTimes).toEqual(1); |
|
|
|
expect(wrapper.find('input').props().value).toEqual('a'); |
|
|
|
}); |
|
|
|
}); |
|
|
|