Skip to content

Commit 7e6ce15

Browse files
committed
fix: loop logic
1 parent ce7b4b1 commit 7e6ce15

File tree

3 files changed

+60
-36
lines changed

3 files changed

+60
-36
lines changed

src/useForm.ts

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -885,27 +885,14 @@ export class FormStore {
885885
const TMP_SPLIT = String(Date.now());
886886
const validateNamePathList = new Set<string>();
887887

888+
const recursive = options?.recursive;
889+
888890
this.getFieldEntities(true).forEach((field: FieldEntity) => {
889891
// Add field if not provide `nameList`
890892
if (!provideNameList) {
891893
namePathList.push(field.getNamePath());
892894
}
893895

894-
/**
895-
* Recursive validate if configured.
896-
* TODO: perf improvement @zombieJ
897-
*/
898-
if (options?.recursive && provideNameList) {
899-
const namePath = field.getNamePath();
900-
if (
901-
// nameList[i] === undefined 说明是以 nameList 开头的
902-
// ['name'] -> ['name','list']
903-
namePath.every((nameUnit, i) => nameList[i] === nameUnit || nameList[i] === undefined)
904-
) {
905-
namePathList.push(namePath);
906-
}
907-
}
908-
909896
// Skip if without rule
910897
if (!field.props.rules || !field.props.rules.length) {
911898
return;
@@ -915,7 +902,7 @@ export class FormStore {
915902
validateNamePathList.add(fieldNamePath.join(TMP_SPLIT));
916903

917904
// Add field validate rule in to promise list
918-
if (!provideNameList || containsNamePath(namePathList, fieldNamePath)) {
905+
if (!provideNameList || containsNamePath(namePathList, fieldNamePath, recursive)) {
919906
const promise = field.validateRules({
920907
validateMessages: {
921908
...defaultValidateMessages,

src/utils/valueUtil.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,40 @@ export function cloneByNamePathList(store: Store, namePathList: InternalNamePath
2626
return newStore;
2727
}
2828

29-
export function containsNamePath(namePathList: InternalNamePath[], namePath: InternalNamePath) {
30-
return namePathList && namePathList.some(path => matchNamePath(path, namePath));
29+
/**
30+
* Check if `namePathList` includes `namePath`.
31+
* @param namePathList A list of `InternalNamePath[]`
32+
* @param namePath Compare `InternalNamePath`
33+
* @param partialMatch True will make `[a, b]` match `[a, b, c]`
34+
*/
35+
export function containsNamePath(
36+
namePathList: InternalNamePath[],
37+
namePath: InternalNamePath,
38+
partialMatch = false,
39+
) {
40+
return namePathList && namePathList.some(path => matchNamePath(namePath, path, partialMatch));
3141
}
3242

43+
/**
44+
* Check if `namePath` is super set or equal of `subNamePath`.
45+
* @param namePath A list of `InternalNamePath[]`
46+
* @param subNamePath Compare `InternalNamePath`
47+
* @param partialMatch True will make `[a, b]` match `[a, b, c]`
48+
*/
3349
export function matchNamePath(
3450
namePath: InternalNamePath,
35-
changedNamePath: InternalNamePath | null,
51+
subNamePath: InternalNamePath | null,
52+
partialMatch = false,
3653
) {
37-
if (!namePath || !changedNamePath || namePath.length !== changedNamePath.length) {
54+
if (!namePath || !subNamePath) {
3855
return false;
3956
}
40-
return namePath.every((nameUnit, i) => changedNamePath[i] === nameUnit);
57+
58+
if (!partialMatch && namePath.length !== subNamePath.length) {
59+
return false;
60+
}
61+
62+
return subNamePath.every((nameUnit, i) => namePath[i] === nameUnit);
4163
}
4264

4365
// Like `shallowEqual`, but we not check the data which may cause re-render

tests/validate.test.tsx

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useEffect } from 'react';
2-
import { render } from '@testing-library/react';
2+
import { fireEvent, render } from '@testing-library/react';
33
import { mount } from 'enzyme';
44
import { act } from 'react-dom/test-utils';
55
import Form, { Field, useForm } from '../src';
@@ -694,8 +694,8 @@ describe('Form.Validate', () => {
694694
});
695695

696696
it('validate support recursive', async () => {
697-
let form;
698-
const wrapper = mount(
697+
let form: FormInstance;
698+
const { container } = render(
699699
<div>
700700
<Form
701701
ref={instance => {
@@ -708,24 +708,39 @@ describe('Form.Validate', () => {
708708
</div>,
709709
);
710710

711-
wrapper
712-
.find('input')
713-
.at(0)
714-
.simulate('change', { target: { value: '' } });
715-
await act(async () => {
716-
await timeout();
717-
});
718-
wrapper.update();
711+
async function changeEmptyValue(input: HTMLElement) {
712+
fireEvent.change(input, {
713+
target: {
714+
value: '2',
715+
},
716+
});
717+
fireEvent.change(input, {
718+
target: {
719+
value: '',
720+
},
721+
});
722+
723+
await act(async () => {
724+
await timeout();
725+
});
726+
}
727+
728+
await changeEmptyValue(container.querySelector('input'));
719729

720730
try {
721-
const values = await form.validateFields(['username'], { recursive: true });
722-
expect(values.username.do).toBe('');
731+
await form.validateFields([['username']], { recursive: true } as any);
732+
733+
// Should not reach this
734+
expect(false).toBeTruthy();
723735
} catch (error) {
724736
expect(error.errorFields.length).toBe(2);
737+
expect(error.errorFields[0].errors).toEqual(["'username.do' is required"]);
738+
expect(error.errorFields[1].errors).toEqual(["'username.list' is required"]);
725739
}
726740

727-
const values = await form.validateFields(['username']);
728-
expect(values.username.do).toBe('');
741+
await act(async () => {
742+
await timeout();
743+
});
729744
});
730745

731746
it('not trigger validator', async () => {

0 commit comments

Comments
 (0)