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 RcFieldProps = Omit<FieldProps, 'children'>;
type ChildrenType = React.ReactElement | RenderChildren | React.ReactElement[] | null; 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 export interface FormItemProps
extends FormItemLabelProps, extends FormItemLabelProps,
FormItemInputProps, FormItemInputProps,
@ -216,6 +231,10 @@ function FormItem(props: FormItemProps): React.ReactElement {
return renderLayout(children); return renderLayout(children);
} }
// Record for real component render
const updateRef = React.useRef(0);
updateRef.current += 1;
return ( return (
<Field <Field
{...props} {...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) { } else if (isRenderProps && shouldUpdate && !hasName) {
childNode = (children as RenderChildren)(context); childNode = (children as RenderChildren)(context);
} else { } else {

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

@ -621,4 +621,35 @@ describe('Form', () => {
} }
expect(instances.size).toEqual(1); 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