Skip to content

Commit 3f519e1

Browse files
authored
refactor: Field init to set values (#207)
* Wrap context to upder wrap field component to aovid first flash * fix update logic * test: add test case * chore: Update deps * chore: Update peer deps * chore: Update peer deps
1 parent 9e217f4 commit 3f519e1

File tree

5 files changed

+101
-24
lines changed

5 files changed

+101
-24
lines changed

examples/reset.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ const Demo = () => {
3636
<Item name={['path1', 'path2']} rules={[{ required: true }]}>
3737
<Input placeholder="nest" />
3838
</Item>
39+
<Item name={['path1', 'path3']} initialValue="bamboo">
40+
<Input placeholder="nest" />
41+
</Item>
3942
<button
4043
type="button"
4144
onClick={() => {

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
"now-build": "npm run build"
4141
},
4242
"peerDependencies": {
43-
"react": "*"
43+
"react": "^16.9.0"
4444
},
4545
"devDependencies": {
4646
"@types/enzyme": "^3.10.5",
@@ -53,10 +53,10 @@
5353
"enzyme-to-json": "^3.1.4",
5454
"father": "^2.13.6",
5555
"np": "^5.0.3",
56-
"react": "^v16.9.0-alpha.0",
56+
"react": "^16.14.0",
5757
"react-dnd": "^8.0.3",
5858
"react-dnd-html5-backend": "^8.0.3",
59-
"react-dom": "^v16.9.0-alpha.0",
59+
"react-dom": "^16.14.0",
6060
"react-redux": "^4.4.10",
6161
"react-router": "^3.0.0",
6262
"redux": "^3.7.2",

src/Field.tsx

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,14 @@ export interface InternalFieldProps<Values = any> {
7676

7777
/** @private Passed by Form.List props. Do not use since it will break by path check. */
7878
isListField?: boolean;
79+
80+
/** @private Pass context as prop instead of context api
81+
* since class component can not get context in constructor */
82+
fieldContext: InternalFormInstance;
7983
}
8084

81-
export interface FieldProps<Values = any> extends Omit<InternalFieldProps<Values>, 'name'> {
85+
export interface FieldProps<Values = any>
86+
extends Omit<InternalFieldProps<Values>, 'name' | 'fieldContext'> {
8287
name?: NamePath;
8388
}
8489

@@ -87,24 +92,21 @@ export interface FieldState {
8792
}
8893

8994
// We use Class instead of Hooks here since it will cost much code by using Hooks.
90-
class Field extends React.Component<InternalFieldProps, FieldState, InternalFormInstance>
91-
implements FieldEntity {
95+
class Field extends React.Component<InternalFieldProps, FieldState> implements FieldEntity {
9296
public static contextType = FieldContext;
9397

9498
public static defaultProps = {
9599
trigger: 'onChange',
96100
valuePropName: 'value',
97101
};
98102

99-
context: InternalFormInstance;
100-
101103
public state = {
102104
resetCount: 0,
103105
};
104106

105107
private cancelRegisterFunc: (isListField?: boolean, preserve?: boolean) => void | null = null;
106108

107-
private destroy = false;
109+
private mounted = false;
108110

109111
/**
110112
* Follow state should not management in State since it will async update by React.
@@ -122,11 +124,21 @@ class Field extends React.Component<InternalFieldProps, FieldState, InternalForm
122124
private errors: string[] = [];
123125

124126
// ============================== Subscriptions ==============================
127+
constructor(props: InternalFieldProps) {
128+
super(props);
129+
130+
// Register on init
131+
if (props.fieldContext) {
132+
const { getInternalHooks }: InternalFormInstance = props.fieldContext;
133+
const { registerField } = getInternalHooks(HOOK_MARK);
134+
this.cancelRegisterFunc = registerField(this);
135+
}
136+
}
137+
125138
public componentDidMount() {
126139
const { shouldUpdate } = this.props;
127-
const { getInternalHooks }: InternalFormInstance = this.context;
128-
const { registerField } = getInternalHooks(HOOK_MARK);
129-
this.cancelRegisterFunc = registerField(this);
140+
141+
this.mounted = true;
130142

131143
// One more render for component in case fields not ready
132144
if (shouldUpdate === true) {
@@ -136,7 +148,7 @@ class Field extends React.Component<InternalFieldProps, FieldState, InternalForm
136148

137149
public componentWillUnmount() {
138150
this.cancelRegister();
139-
this.destroy = true;
151+
this.mounted = false;
140152
}
141153

142154
public cancelRegister = () => {
@@ -150,32 +162,32 @@ class Field extends React.Component<InternalFieldProps, FieldState, InternalForm
150162

151163
// ================================== Utils ==================================
152164
public getNamePath = (): InternalNamePath => {
153-
const { name } = this.props;
154-
const { prefixName = [] }: InternalFormInstance = this.context;
165+
const { name, fieldContext } = this.props;
166+
const { prefixName = [] }: InternalFormInstance = fieldContext;
155167

156168
return name !== undefined ? [...prefixName, ...name] : [];
157169
};
158170

159171
public getRules = (): RuleObject[] => {
160-
const { rules = [] } = this.props;
172+
const { rules = [], fieldContext } = this.props;
161173

162174
return rules.map(
163175
(rule: Rule): RuleObject => {
164176
if (typeof rule === 'function') {
165-
return rule(this.context);
177+
return rule(fieldContext);
166178
}
167179
return rule;
168180
},
169181
);
170182
};
171183

172184
public reRender() {
173-
if (this.destroy) return;
185+
if (!this.mounted) return;
174186
this.forceUpdate();
175187
}
176188

177189
public refresh = () => {
178-
if (this.destroy) return;
190+
if (!this.mounted) return;
179191

180192
/**
181193
* Clean up current node.
@@ -373,7 +385,7 @@ class Field extends React.Component<InternalFieldProps, FieldState, InternalForm
373385
const meta = this.getMeta();
374386

375387
return {
376-
...this.getOnlyChild(children(this.getControlled(), meta, this.context)),
388+
...this.getOnlyChild(children(this.getControlled(), meta, this.props.fieldContext)),
377389
isFunction: true,
378390
};
379391
}
@@ -389,7 +401,7 @@ class Field extends React.Component<InternalFieldProps, FieldState, InternalForm
389401

390402
// ============================== Field Control ==============================
391403
public getValue = (store?: Store) => {
392-
const { getFieldsValue }: FormInstance = this.context;
404+
const { getFieldsValue }: FormInstance = this.props.fieldContext;
393405
const namePath = this.getNamePath();
394406
return getValue(store || getFieldsValue(true), namePath);
395407
};
@@ -402,13 +414,14 @@ class Field extends React.Component<InternalFieldProps, FieldState, InternalForm
402414
normalize,
403415
valuePropName,
404416
getValueProps,
417+
fieldContext,
405418
} = this.props;
406419

407420
const mergedValidateTrigger =
408-
validateTrigger !== undefined ? validateTrigger : this.context.validateTrigger;
421+
validateTrigger !== undefined ? validateTrigger : fieldContext.validateTrigger;
409422

410423
const namePath = this.getNamePath();
411-
const { getInternalHooks, getFieldsValue }: InternalFormInstance = this.context;
424+
const { getInternalHooks, getFieldsValue }: InternalFormInstance = fieldContext;
412425
const { dispatch } = getInternalHooks(HOOK_MARK);
413426
const value = this.getValue();
414427
const mergedGetValueProps = getValueProps || ((val: StoreValue) => ({ [valuePropName]: val }));
@@ -502,6 +515,8 @@ class Field extends React.Component<InternalFieldProps, FieldState, InternalForm
502515
}
503516

504517
function WrapperField<Values = any>({ name, ...restProps }: FieldProps<Values>) {
518+
const fieldContext = React.useContext(FieldContext);
519+
505520
const namePath = name !== undefined ? getNamePath(name) : undefined;
506521

507522
let key: string = 'keep';
@@ -516,7 +531,7 @@ function WrapperField<Values = any>({ name, ...restProps }: FieldProps<Values>)
516531
);
517532
}
518533

519-
return <Field key={key} name={namePath} {...restProps} />;
534+
return <Field key={key} name={namePath} {...restProps} fieldContext={fieldContext} />;
520535
}
521536

522537
export default WrapperField;

tests/field.test.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import React from 'react';
2+
import { mount } from 'enzyme';
3+
import Form, { Field } from '../src';
4+
5+
describe('Form.Field', () => {
6+
it('field remount should trigger constructor again', () => {
7+
const Demo = ({ visible }: { visible: boolean }) => {
8+
const [form] = Form.useForm();
9+
10+
const fieldNode = <Field name="light" initialValue="bamboo" />;
11+
12+
return <Form form={form}>{visible ? fieldNode : null}</Form>;
13+
};
14+
15+
// First mount
16+
const wrapper = mount(<Demo visible />);
17+
const instance = wrapper.find('Field').instance() as any;
18+
expect(instance.cancelRegisterFunc).toBeTruthy();
19+
20+
// Hide
21+
wrapper.setProps({ visible: false });
22+
expect(instance.cancelRegisterFunc).toBeFalsy();
23+
24+
// Mount again
25+
wrapper.setProps({ visible: true });
26+
expect(instance.cancelRegisterFunc).toBeFalsy();
27+
expect((wrapper.find('Field').instance() as any).cancelRegisterFunc).toBeTruthy();
28+
});
29+
});

tests/initialValue.test.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,5 +306,35 @@ describe('Form.InitialValues', () => {
306306
wrapper.find('button').simulate('click');
307307
expect(wrapper.find('input').props().value).toEqual('bamboo');
308308
});
309+
310+
it('not initialValue when not mount', () => {
311+
let formInstance;
312+
313+
const Test = () => {
314+
const [form] = Form.useForm();
315+
formInstance = form;
316+
317+
const fieldNode = <Field name="bamboo" initialValue="light" />;
318+
319+
expect(fieldNode).toBeTruthy();
320+
321+
return (
322+
<Form form={form}>
323+
<Field name="light" initialValue="bamboo">
324+
{control => {
325+
expect(control.value).toEqual('bamboo');
326+
return null;
327+
}}
328+
</Field>
329+
</Form>
330+
);
331+
};
332+
333+
const wrapper = mount(<Test />);
334+
335+
expect(formInstance.getFieldsValue()).toEqual({ light: 'bamboo' });
336+
337+
wrapper.unmount();
338+
});
309339
});
310340
});

0 commit comments

Comments
 (0)