mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-28 21:19:37 +08:00
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
This commit is contained in:
parent
76de873d87
commit
9fdb82b7df
@ -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');
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user