Skip to content

Commit 850f91f

Browse files
authored
fix: dependencies should work when field is already validated (#129)
* add dirty mark * add test case
1 parent 1e40a57 commit 850f91f

File tree

5 files changed

+66
-1
lines changed

5 files changed

+66
-1
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"react": "*"
4444
},
4545
"devDependencies": {
46+
"@types/enzyme": "^3.10.5",
4647
"@types/jest": "^25.2.1",
4748
"@types/lodash": "^4.14.135",
4849
"@types/react": "^16.8.19",

src/Field.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ class Field extends React.Component<InternalFieldProps, FieldState> implements F
106106
*/
107107
private touched: boolean = false;
108108

109+
/** Mark when touched & validated. Currently only used for `dependencies` */
110+
private dirty: boolean = false;
111+
109112
private validatePromise: Promise<string[]> | null = null;
110113

111114
private prevValidating: boolean;
@@ -189,6 +192,7 @@ class Field extends React.Component<InternalFieldProps, FieldState> implements F
189192
// `setFieldsValue` is a quick access to update related status
190193
if (info.type === 'valueUpdate' && info.source === 'external' && prevValue !== curValue) {
191194
this.touched = true;
195+
this.dirty = true;
192196
this.validatePromise = null;
193197
this.errors = [];
194198
}
@@ -198,6 +202,7 @@ class Field extends React.Component<InternalFieldProps, FieldState> implements F
198202
if (!namePathList || namePathMatch) {
199203
// Clean up state
200204
this.touched = false;
205+
this.dirty = false;
201206
this.validatePromise = null;
202207
this.errors = [];
203208

@@ -222,6 +227,7 @@ class Field extends React.Component<InternalFieldProps, FieldState> implements F
222227
if ('errors' in data) {
223228
this.errors = data.errors || [];
224229
}
230+
this.dirty = true;
225231

226232
this.reRender();
227233
return;
@@ -304,6 +310,7 @@ class Field extends React.Component<InternalFieldProps, FieldState> implements F
304310
validateFirst,
305311
messageVariables,
306312
);
313+
this.dirty = true;
307314
this.validatePromise = promise;
308315
this.errors = [];
309316

@@ -324,6 +331,8 @@ class Field extends React.Component<InternalFieldProps, FieldState> implements F
324331

325332
public isFieldTouched = () => this.touched;
326333

334+
public isFieldDirty = () => this.dirty;
335+
327336
public getErrors = () => this.errors;
328337

329338
// ============================= Child Component =============================
@@ -400,6 +409,7 @@ class Field extends React.Component<InternalFieldProps, FieldState> implements F
400409
control[trigger] = (...args: EventArgs) => {
401410
// Mark as touched
402411
this.touched = true;
412+
this.dirty = true;
403413

404414
let newValue: StoreValue;
405415
if (getValueFromEvent) {

src/interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export interface ValidateErrorEntity {
8585
export interface FieldEntity {
8686
onStoreChange: (store: Store, namePathList: InternalNamePath[] | null, info: NotifyInfo) => void;
8787
isFieldTouched: () => boolean;
88+
isFieldDirty: () => boolean;
8889
isFieldValidating: () => boolean;
8990
validateRules: (options?: ValidateOptions) => Promise<string[]>;
9091
getMeta: () => Meta;

src/useForm.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ export class FormStore {
585585
children.add(field);
586586

587587
const fieldNamePath = field.getNamePath();
588-
if (field.isFieldTouched() && fieldNamePath.length) {
588+
if (field.isFieldDirty() && fieldNamePath.length) {
589589
childrenFields.push(fieldNamePath);
590590
fillChildren(fieldNamePath);
591591
}

tests/dependencies.test.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react';
22
import { mount } from 'enzyme';
33
import Form, { Field } from '../src';
4+
import timeout from './common/timeout';
45
import InfoField, { Input } from './common/InfoField';
56
import { changeValue, matchError, getField } from './common';
67

@@ -69,4 +70,56 @@ describe('Form.Dependencies', () => {
6970

7071
expect(rendered).toBeTruthy();
7172
});
73+
74+
it('should work when field is dirty', async () => {
75+
let pass = false;
76+
77+
const wrapper = mount(
78+
<Form>
79+
<InfoField
80+
name="field_1"
81+
rules={[
82+
{
83+
validator: () => {
84+
if (pass) {
85+
return Promise.resolve();
86+
}
87+
return Promise.reject('You should not pass');
88+
},
89+
},
90+
]}
91+
dependencies={['field_2']}
92+
/>
93+
94+
<InfoField name="field_2" />
95+
96+
<Field shouldUpdate>
97+
{(_, __, { resetFields }) => (
98+
<button
99+
type="button"
100+
onClick={() => {
101+
resetFields();
102+
}}
103+
/>
104+
)}
105+
</Field>
106+
</Form>,
107+
);
108+
109+
wrapper.find('form').simulate('submit');
110+
await timeout();
111+
wrapper.update();
112+
matchError(getField(wrapper, 0), 'You should not pass');
113+
114+
// Mock new validate
115+
pass = true;
116+
await changeValue(getField(wrapper, 1), 'bamboo');
117+
matchError(getField(wrapper, 0), false);
118+
119+
// Should not validate after reset
120+
pass = false;
121+
wrapper.find('button').simulate('click');
122+
await changeValue(getField(wrapper, 1), 'light');
123+
matchError(getField(wrapper, 0), false);
124+
});
72125
});

0 commit comments

Comments
 (0)