Skip to content

Commit d40d882

Browse files
committed
fix shouldUpdate logic
1 parent 8738319 commit d40d882

File tree

4 files changed

+85
-19
lines changed

4 files changed

+85
-19
lines changed

examples/StateForm-validate-perf.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable react/prop-types */
22

33
import React from 'react';
4-
import Form, { FormInstance } from '../src/';
4+
import Form, { Field, FormInstance } from '../src/';
55
import Input from './components/Input';
66
import LabelField from './components/LabelField';
77
import { ValidateMessages } from '../src/interface';
@@ -78,7 +78,19 @@ export default class Demo extends React.Component {
7878
<Input />
7979
</LabelField>
8080

81-
<button type="submit">Submit</button>
81+
<Field shouldUpdate>
82+
{(_, __, { getFieldsError, isFieldsTouched }) => {
83+
const isAllTouched = isFieldsTouched(true);
84+
const hasErrors = !!getFieldsError().filter(({ errors }) => errors.length).length;
85+
86+
return (
87+
<button type="submit" disabled={!isAllTouched || hasErrors}>
88+
Submit
89+
</button>
90+
);
91+
}}
92+
</Field>
93+
8294
<button
8395
type="button"
8496
onClick={() => {

src/Field.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ class Field extends React.Component<FieldProps, FieldState> implements FieldEnti
172172
this.validatePromise = null;
173173

174174
this.refresh();
175+
return;
175176
}
176177
break;
177178

@@ -186,6 +187,7 @@ class Field extends React.Component<FieldProps, FieldState> implements FieldEnti
186187
}
187188

188189
this.refresh();
190+
return;
189191
}
190192
break;
191193
}
@@ -196,6 +198,7 @@ class Field extends React.Component<FieldProps, FieldState> implements FieldEnti
196198

197199
if (this.prevValidating !== validating || !isSimilar(this.prevErrors, errors)) {
198200
this.reRender();
201+
return;
199202
}
200203
break;
201204
}
@@ -210,6 +213,7 @@ class Field extends React.Component<FieldProps, FieldState> implements FieldEnti
210213
dependencyList.some(dependency => containsNamePath(info.relatedFields, dependency))
211214
) {
212215
this.reRender();
216+
return;
213217
}
214218
break;
215219
}
@@ -226,15 +230,19 @@ class Field extends React.Component<FieldProps, FieldState> implements FieldEnti
226230
dependencies.some(dependency =>
227231
containsNamePath(namePathList, getNamePath(dependency)),
228232
) ||
229-
shouldUpdate === true ||
230233
(typeof shouldUpdate === 'function'
231234
? shouldUpdate(prevStore, values, info)
232235
: prevValue !== curValue)
233236
) {
234237
this.reRender();
238+
return;
235239
}
236240
break;
237241
}
242+
243+
if (shouldUpdate === true) {
244+
this.reRender();
245+
}
238246
};
239247

240248
public isFieldTouched = () => this.touched;

src/interface.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { ReactElement } from 'react';
12
import { ReducerAction } from './useForm';
23

34
export type InternalNamePath = (string | number)[];
@@ -50,7 +51,7 @@ interface BaseRule {
5051
enum?: StoreValue[];
5152
len?: number;
5253
max?: number;
53-
message?: string;
54+
message?: string | ReactElement;
5455
min?: number;
5556
pattern?: RegExp;
5657
required?: boolean;
@@ -142,17 +143,14 @@ export interface InternalHooks {
142143
setValidateMessages: (validateMessages: ValidateMessages) => void;
143144
}
144145

145-
export type IsFieldsTouched =
146-
| ((allFieldsTouched?: boolean) => boolean)
147-
| ((nameList: NamePath[], allFieldsTouched?: boolean) => boolean);
148-
149146
export interface FormInstance {
150147
// Origin Form API
151148
getFieldValue: (name: NamePath) => StoreValue;
152149
getFieldsValue: (nameList?: NamePath[]) => Store;
153150
getFieldError: (name: NamePath) => string[];
154151
getFieldsError: (nameList?: NamePath[]) => FieldError[];
155-
isFieldsTouched: IsFieldsTouched;
152+
isFieldsTouched(nameList?: NamePath[], allFieldsTouched?: boolean): boolean;
153+
isFieldsTouched(allFieldsTouched?: boolean): boolean;
156154
isFieldTouched: (name: NamePath) => boolean;
157155
isFieldValidating: (name: NamePath) => boolean;
158156
isFieldsValidating: (nameList: NamePath[]) => boolean;

tests/index.test.js

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,39 @@ describe('Basic', () => {
130130

131131
resetTest('with field name', ['username']);
132132
resetTest('without field name');
133+
134+
it('not affect others', async () => {
135+
let form;
136+
137+
const wrapper = mount(
138+
<div>
139+
<Form
140+
ref={instance => {
141+
form = instance;
142+
}}
143+
>
144+
<Field name="username" rules={[{ required: true }]}>
145+
<Input />
146+
</Field>
147+
148+
<Field name="password" rules={[{ required: true }]}>
149+
<Input />
150+
</Field>
151+
</Form>
152+
</div>,
153+
);
154+
155+
await changeValue(getField(wrapper, 'username'), 'Bamboo');
156+
await changeValue(getField(wrapper, 'password'), '');
157+
form.resetFields(['username']);
158+
159+
expect(form.getFieldValue('username')).toEqual(undefined);
160+
expect(form.getFieldError('username')).toEqual([]);
161+
expect(form.isFieldTouched('username')).toBeFalsy();
162+
expect(form.getFieldValue('password')).toEqual('');
163+
expect(form.getFieldError('password')).toEqual(["'password' is required"]);
164+
expect(form.isFieldTouched('password')).toBeTruthy();
165+
});
133166
});
134167

135168
describe('initialValues', () => {
@@ -320,28 +353,43 @@ describe('Basic', () => {
320353
errorSpy.mockRestore();
321354
});
322355

323-
it('shouldUpdate return true will update whatever notification updated', async () => {
324-
let renderCount = 0;
356+
it('shouldUpdate', async () => {
357+
let isAllTouched;
358+
let hasError;
325359

326360
const wrapper = mount(
327361
<Form>
328-
<Field rules={[{ required: true }]}>
362+
<Field name="username" rules={[{ required: true }]}>
363+
<Input />
364+
</Field>
365+
<Field name="password" rules={[{ required: true }]}>
329366
<Input />
330367
</Field>
331368
<Field shouldUpdate>
332-
{() => {
333-
renderCount += 1;
369+
{(_, __, { getFieldsError, isFieldsTouched }) => {
370+
isAllTouched = isFieldsTouched(true);
371+
hasError = getFieldsError().filter(({ errors }) => errors.length).length;
372+
334373
return null;
335374
}}
336375
</Field>
337376
</Form>,
338377
);
339378

340-
const baseRenderCount = renderCount;
341-
changeValue(getField(wrapper), '');
342-
expect(renderCount).toEqual(baseRenderCount + 1);
379+
await changeValue(getField(wrapper, 'username'), '');
380+
expect(isAllTouched).toBeFalsy();
381+
expect(hasError).toBeTruthy();
343382

344-
await timeout();
345-
expect(renderCount).toEqual(baseRenderCount + 2);
383+
await changeValue(getField(wrapper, 'username'), 'Bamboo');
384+
expect(isAllTouched).toBeFalsy();
385+
expect(hasError).toBeFalsy();
386+
387+
await changeValue(getField(wrapper, 'password'), 'Light');
388+
expect(isAllTouched).toBeTruthy();
389+
expect(hasError).toBeFalsy();
390+
391+
await changeValue(getField(wrapper, 'password'), '');
392+
expect(isAllTouched).toBeTruthy();
393+
expect(hasError).toBeTruthy();
346394
});
347395
});

0 commit comments

Comments
 (0)