Skip to content

Commit 6ab530f

Browse files
authored
fix: control fields should correct handle error messages (#109)
* pass origin mark * skip refresh * fix error passing logic * test case
1 parent 8d9f2b9 commit 6ab530f

File tree

5 files changed

+66
-16
lines changed

5 files changed

+66
-16
lines changed

examples/redux.tsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ let App: any = ({ dispatch, fields }) => {
3636
}}
3737
>
3838
<h3>Redux Form</h3>
39-
<p>It's no need to put data into redux store. But you can still do this.</p>
39+
<p>It is no need to put data into redux store. But you can still do this.</p>
4040

4141
<LabelField name="field">
4242
<Input />
@@ -76,12 +76,10 @@ let App: any = ({ dispatch, fields }) => {
7676
};
7777
App = connect((fields: any) => ({ fields }))(App);
7878

79-
const Demo = () => {
80-
return (
81-
<Provider store={store}>
82-
<App />
83-
</Provider>
84-
);
85-
};
79+
const Demo = () => (
80+
<Provider store={store}>
81+
<App />
82+
</Provider>
83+
);
8684

8785
export default Demo;

src/Field.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ class Field extends React.Component<InternalFieldProps, FieldState> implements F
208208
if ('touched' in data) {
209209
this.touched = data.touched;
210210
}
211-
if ('validating' in data) {
211+
if ('validating' in data && !('originRCField' in data)) {
212212
this.validatePromise = data.validating ? Promise.resolve([]) : null;
213213
}
214214
if ('errors' in data) {

src/interface.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@ export interface Meta {
1616
name: InternalNamePath;
1717
}
1818

19+
export interface InternalFieldData extends Meta {
20+
value: StoreValue;
21+
}
22+
1923
/**
2024
* Used by `setFields` config
2125
*/
22-
export interface FieldData extends Partial<Omit<Meta, 'name'>> {
26+
export interface FieldData extends Partial<Omit<InternalFieldData, 'name'>> {
2327
name: NamePath;
24-
value?: StoreValue;
2528
}
2629

2730
export type RuleType =

src/useForm.ts

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
ValidateErrorEntity,
1919
StoreValue,
2020
Meta,
21+
InternalFieldData,
2122
} from './interface';
2223
import { HOOK_MARK } from './FieldContext';
2324
import { allPromiseFinish } from './utils/asyncUtil';
@@ -347,16 +348,22 @@ export class FormStore {
347348
});
348349
};
349350

350-
private getFields = (): FieldData[] =>
351+
private getFields = (): InternalFieldData[] =>
351352
this.getFieldEntities(true).map(
352-
(field: FieldEntity): FieldData => {
353+
(field: FieldEntity): InternalFieldData => {
353354
const namePath = field.getNamePath();
354355
const meta = field.getMeta();
355-
return {
356+
const fieldData = {
356357
...meta,
357358
name: namePath,
358359
value: this.getFieldValue(namePath),
359360
};
361+
362+
Object.defineProperty(fieldData, 'originRCField', {
363+
value: true,
364+
});
365+
366+
return fieldData;
360367
},
361368
);
362369

@@ -487,11 +494,30 @@ export class FormStore {
487494
return childrenFields;
488495
};
489496

490-
private triggerOnFieldsChange = (namePathList: InternalNamePath[]) => {
497+
private triggerOnFieldsChange = (
498+
namePathList: InternalNamePath[],
499+
filedErrors?: FieldError[],
500+
) => {
491501
const { onFieldsChange } = this.callbacks;
492502

493503
if (onFieldsChange) {
494504
const fields = this.getFields();
505+
506+
/**
507+
* Fill errors since `fields` may be replaced by controlled fields
508+
*/
509+
if (filedErrors) {
510+
const cache = new NameMap<string[]>();
511+
filedErrors.forEach(({ name, errors }) => {
512+
cache.set(name, errors);
513+
});
514+
515+
fields.forEach(field => {
516+
// eslint-disable-next-line no-param-reassign
517+
field.errors = cache.get(field.name) || field.errors;
518+
});
519+
}
520+
495521
const changedFields = fields.filter(({ name: fieldName }) =>
496522
containsNamePath(namePathList, fieldName as InternalNamePath),
497523
);
@@ -565,7 +591,7 @@ export class FormStore {
565591
this.notifyObservers(this.store, resultNamePathList, {
566592
type: 'validateFinish',
567593
});
568-
this.triggerOnFieldsChange(resultNamePathList);
594+
this.triggerOnFieldsChange(resultNamePathList, results);
569595
});
570596

571597
const returnPromise: Promise<Store | ValidateErrorEntity | string[]> = summaryPromise

tests/control.test.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react';
22
import { mount } from 'enzyme';
33
import Form from '../src';
44
import InfoField from './common/InfoField';
5+
import { changeValue, matchError } from './common';
56

67
describe('Form.Control', () => {
78
it('fields', () => {
@@ -18,4 +19,26 @@ describe('Form.Control', () => {
1819

1920
expect(wrapper.find('input').props().value).toEqual('Bamboo');
2021
});
22+
23+
it('fully test', async () => {
24+
const Test = () => {
25+
const [fields, setFields] = React.useState([]);
26+
27+
return (
28+
<Form
29+
fields={fields}
30+
onFieldsChange={(_, allFields) => {
31+
setFields(allFields);
32+
}}
33+
>
34+
<InfoField name="test" rules={[{ required: true }]} />
35+
</Form>
36+
);
37+
};
38+
39+
const wrapper = mount(<Test />);
40+
41+
await changeValue(wrapper, '');
42+
matchError(wrapper, "'test' is required");
43+
});
2144
});

0 commit comments

Comments
 (0)