Skip to content

Commit a9a473b

Browse files
committed
fix(perf): avoid generating path lookup too often #4382
1 parent c511385 commit a9a473b

File tree

3 files changed

+48
-3
lines changed

3 files changed

+48
-3
lines changed

.changeset/sixty-bees-mix.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'vee-validate': patch
3+
---
4+
5+
feat(perf): improve performance setFieldError and setFieldValue closes #4382

packages/vee-validate/src/useForm.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ import {
6565
normalizeErrorItem,
6666
normalizeEventValue,
6767
omit,
68+
debounceNextTick,
6869
} from './utils';
6970
import { FormContextKey } from './symbols';
7071
import { validateTypedSchema, validateObjectSchema } from './validate';
@@ -136,8 +137,10 @@ export function useForm<
136137

137138
const extraErrorsBag: Ref<FormErrorBag<TValues>> = ref({});
138139

139-
const pathStateLookup = computed<Record<string, PathState>>(() => {
140-
return pathStates.value.reduce(
140+
const pathStateLookup = ref<Record<string, PathState>>({});
141+
142+
const rebuildPathLookup = debounceNextTick(() => {
143+
pathStateLookup.value = pathStates.value.reduce(
141144
(names, state) => {
142145
names[normalizeFormPath(toValue(state.path))] = state;
143146

@@ -314,6 +317,8 @@ export function useForm<
314317
}) as PathState<TValue>;
315318

316319
pathStates.value.push(state);
320+
pathStateLookup.value[pathValue] = state;
321+
rebuildPathLookup();
317322

318323
if (errors.value[pathValue] && !initialErrors[pathValue]) {
319324
nextTick(() => {
@@ -324,7 +329,9 @@ export function useForm<
324329
// Handles when a path changes
325330
if (isRef(path)) {
326331
watch(path, newPath => {
332+
rebuildPathLookup();
327333
const nextValue = deepCopy(currentValue.value);
334+
pathStateLookup.value[newPath] = state;
328335

329336
nextTick(() => {
330337
setInPath(formValues, newPath, nextValue);
@@ -547,6 +554,8 @@ export function useForm<
547554
if (!pathState.multiple || pathState.fieldsCount <= 0) {
548555
pathStates.value.splice(idx, 1);
549556
unsetInitialValue(path);
557+
rebuildPathLookup();
558+
delete pathStateLookup.value[path];
550559
}
551560
}
552561

packages/vee-validate/src/utils/common.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@ import {
99
warn as vueWarning,
1010
watch,
1111
MaybeRef,
12+
nextTick,
1213
} from 'vue';
1314
import { klona as deepCopy } from 'klona/full';
1415
import { isIndex, isNullOrUndefined, isObject, toNumber } from '../../../shared';
1516
import { isContainerValue, isEmptyContainer, isEqual, isNotNestedPath } from './assertions';
16-
import { GenericObject } from '../types';
17+
import { GenericObject, MaybePromise } from '../types';
1718
import { FormContextKey, FieldContextKey } from '../symbols';
1819

1920
function cleanupNonNestedPath(path: string) {
@@ -327,3 +328,33 @@ export function omit<TObj extends GenericObject>(obj: TObj, keys: (keyof Generic
327328

328329
return target;
329330
}
331+
332+
export function debounceNextTick<
333+
TFunction extends (...args: any[]) => MaybePromise<any>,
334+
TResult = ReturnType<TFunction>,
335+
>(inner: TFunction): (...args: Parameters<TFunction>) => Promise<TResult> {
336+
let lastTick: Promise<any> | null = null;
337+
let resolves: any[] = [];
338+
339+
return function (...args: Parameters<TFunction>) {
340+
// Run the function after a certain amount of time
341+
342+
const thisTick = nextTick(() => {
343+
if (lastTick !== thisTick) {
344+
return;
345+
}
346+
347+
// Get the result of the inner function, then apply it to the resolve function of
348+
// each promise that has been created since the last time the inner function was run
349+
const result = inner(...(args as any));
350+
351+
resolves.forEach(r => r(result));
352+
resolves = [];
353+
lastTick = null;
354+
});
355+
356+
lastTick = thisTick;
357+
358+
return new Promise<TResult>(resolve => resolves.push(resolve));
359+
};
360+
}

0 commit comments

Comments
 (0)