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
parent
commit
9fdb82b7df
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 28
      components/form/FormItem.tsx
  2. 31
      components/form/__tests__/index.test.js

28
components/form/FormItem.tsx

@ -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 {

31
components/form/__tests__/index.test.js

@ -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…
Cancel
Save