diff --git a/examples/reset.tsx b/examples/reset.tsx index fbdc7881..dfa416cd 100644 --- a/examples/reset.tsx +++ b/examples/reset.tsx @@ -36,6 +36,9 @@ const Demo = () => { + + + { diff --git a/package.json b/package.json index 86176628..c39b944b 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "now-build": "npm run build" }, "peerDependencies": { - "react": "*" + "react": "^16.9.0" }, "devDependencies": { "@types/enzyme": "^3.10.5", @@ -53,10 +53,10 @@ "enzyme-to-json": "^3.1.4", "father": "^2.13.6", "np": "^5.0.3", - "react": "^v16.9.0-alpha.0", + "react": "^16.14.0", "react-dnd": "^8.0.3", "react-dnd-html5-backend": "^8.0.3", - "react-dom": "^v16.9.0-alpha.0", + "react-dom": "^16.14.0", "react-redux": "^4.4.10", "react-router": "^3.0.0", "redux": "^3.7.2", diff --git a/src/Field.tsx b/src/Field.tsx index 68822a31..ef14054f 100644 --- a/src/Field.tsx +++ b/src/Field.tsx @@ -76,9 +76,14 @@ export interface InternalFieldProps { /** @private Passed by Form.List props. Do not use since it will break by path check. */ isListField?: boolean; + + /** @private Pass context as prop instead of context api + * since class component can not get context in constructor */ + fieldContext: InternalFormInstance; } -export interface FieldProps extends Omit, 'name'> { +export interface FieldProps + extends Omit, 'name' | 'fieldContext'> { name?: NamePath; } @@ -87,8 +92,7 @@ export interface FieldState { } // We use Class instead of Hooks here since it will cost much code by using Hooks. -class Field extends React.Component - implements FieldEntity { +class Field extends React.Component implements FieldEntity { public static contextType = FieldContext; public static defaultProps = { @@ -96,15 +100,13 @@ class Field extends React.Component void | null = null; - private destroy = false; + private mounted = false; /** * Follow state should not management in State since it will async update by React. @@ -122,11 +124,21 @@ class Field extends React.Component { @@ -150,19 +162,19 @@ class Field extends React.Component { - const { name } = this.props; - const { prefixName = [] }: InternalFormInstance = this.context; + const { name, fieldContext } = this.props; + const { prefixName = [] }: InternalFormInstance = fieldContext; return name !== undefined ? [...prefixName, ...name] : []; }; public getRules = (): RuleObject[] => { - const { rules = [] } = this.props; + const { rules = [], fieldContext } = this.props; return rules.map( (rule: Rule): RuleObject => { if (typeof rule === 'function') { - return rule(this.context); + return rule(fieldContext); } return rule; }, @@ -170,12 +182,12 @@ class Field extends React.Component { - if (this.destroy) return; + if (!this.mounted) return; /** * Clean up current node. @@ -373,7 +385,7 @@ class Field extends React.Component { - const { getFieldsValue }: FormInstance = this.context; + const { getFieldsValue }: FormInstance = this.props.fieldContext; const namePath = this.getNamePath(); return getValue(store || getFieldsValue(true), namePath); }; @@ -402,13 +414,14 @@ class Field extends React.Component ({ [valuePropName]: val })); @@ -502,6 +515,8 @@ class Field extends React.Component({ name, ...restProps }: FieldProps) { + const fieldContext = React.useContext(FieldContext); + const namePath = name !== undefined ? getNamePath(name) : undefined; let key: string = 'keep'; @@ -516,7 +531,7 @@ function WrapperField({ name, ...restProps }: FieldProps) ); } - return ; + return ; } export default WrapperField; diff --git a/tests/field.test.tsx b/tests/field.test.tsx new file mode 100644 index 00000000..5cdf9a54 --- /dev/null +++ b/tests/field.test.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { mount } from 'enzyme'; +import Form, { Field } from '../src'; + +describe('Form.Field', () => { + it('field remount should trigger constructor again', () => { + const Demo = ({ visible }: { visible: boolean }) => { + const [form] = Form.useForm(); + + const fieldNode = ; + + return {visible ? fieldNode : null}; + }; + + // First mount + const wrapper = mount(); + const instance = wrapper.find('Field').instance() as any; + expect(instance.cancelRegisterFunc).toBeTruthy(); + + // Hide + wrapper.setProps({ visible: false }); + expect(instance.cancelRegisterFunc).toBeFalsy(); + + // Mount again + wrapper.setProps({ visible: true }); + expect(instance.cancelRegisterFunc).toBeFalsy(); + expect((wrapper.find('Field').instance() as any).cancelRegisterFunc).toBeTruthy(); + }); +}); diff --git a/tests/initialValue.test.js b/tests/initialValue.test.js index e5f77e4a..f6ced1eb 100644 --- a/tests/initialValue.test.js +++ b/tests/initialValue.test.js @@ -306,5 +306,35 @@ describe('Form.InitialValues', () => { wrapper.find('button').simulate('click'); expect(wrapper.find('input').props().value).toEqual('bamboo'); }); + + it('not initialValue when not mount', () => { + let formInstance; + + const Test = () => { + const [form] = Form.useForm(); + formInstance = form; + + const fieldNode = ; + + expect(fieldNode).toBeTruthy(); + + return ( + + + {control => { + expect(control.value).toEqual('bamboo'); + return null; + }} + + + ); + }; + + const wrapper = mount(); + + expect(formInstance.getFieldsValue()).toEqual({ light: 'bamboo' }); + + wrapper.unmount(); + }); }); });