@tanstack/form-core 0.40.4 → 0.41.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,21 +6,10 @@ const standardSchemaValidator = require("./standardSchemaValidator.cjs");
6
6
  function getDefaultFormState(defaultState) {
7
7
  return {
8
8
  values: defaultState.values ?? {},
9
- errors: defaultState.errors ?? [],
10
9
  errorMap: defaultState.errorMap ?? {},
11
- fieldMeta: defaultState.fieldMeta ?? {},
12
- canSubmit: defaultState.canSubmit ?? true,
13
- isFieldsValid: defaultState.isFieldsValid ?? false,
14
- isFieldsValidating: defaultState.isFieldsValidating ?? false,
15
- isFormValid: defaultState.isFormValid ?? false,
16
- isFormValidating: defaultState.isFormValidating ?? false,
10
+ fieldMetaBase: defaultState.fieldMetaBase ?? {},
17
11
  isSubmitted: defaultState.isSubmitted ?? false,
18
12
  isSubmitting: defaultState.isSubmitting ?? false,
19
- isTouched: defaultState.isTouched ?? false,
20
- isBlurred: defaultState.isBlurred ?? false,
21
- isPristine: defaultState.isPristine ?? true,
22
- isDirty: defaultState.isDirty ?? false,
23
- isValid: defaultState.isValid ?? false,
24
13
  isValidating: defaultState.isValidating ?? false,
25
14
  submissionAttempts: defaultState.submissionAttempts ?? 0,
26
15
  validationMetaMap: defaultState.validationMetaMap ?? {
@@ -45,18 +34,25 @@ class FormApi {
45
34
  this.fieldInfo = {};
46
35
  this.prevTransformArray = [];
47
36
  this.mount = () => {
37
+ const cleanupFieldMetaDerived = this.fieldMetaDerived.mount();
38
+ const cleanupStoreDerived = this.store.mount();
39
+ const cleanup = () => {
40
+ cleanupFieldMetaDerived();
41
+ cleanupStoreDerived();
42
+ };
48
43
  const { onMount } = this.options.validators || {};
49
- if (!onMount) return;
44
+ if (!onMount) return cleanup;
50
45
  this.validateSync("mount");
46
+ return cleanup;
51
47
  };
52
48
  this.update = (options) => {
53
49
  if (!options) return;
54
50
  const oldOptions = this.options;
55
51
  this.options = options;
56
- this.store.batch(() => {
52
+ store.batch(() => {
57
53
  const shouldUpdateValues = options.defaultValues && options.defaultValues !== oldOptions.defaultValues && !this.state.isTouched;
58
54
  const shouldUpdateState = options.defaultState !== oldOptions.defaultState && !this.state.isTouched;
59
- this.store.setState(
55
+ this.baseStore.setState(
60
56
  () => getDefaultFormState(
61
57
  Object.assign(
62
58
  {},
@@ -72,27 +68,27 @@ class FormApi {
72
68
  };
73
69
  this.reset = (values, opts2) => {
74
70
  const { fieldMeta: currentFieldMeta } = this.state;
75
- const fieldMeta = this.resetFieldMeta(currentFieldMeta);
71
+ const fieldMetaBase = this.resetFieldMeta(currentFieldMeta);
76
72
  if (values && !(opts2 == null ? void 0 : opts2.keepDefaultValues)) {
77
73
  this.options = {
78
74
  ...this.options,
79
75
  defaultValues: values
80
76
  };
81
77
  }
82
- this.store.setState(
78
+ this.baseStore.setState(
83
79
  () => {
84
80
  var _a2;
85
81
  return getDefaultFormState({
86
82
  ...this.options.defaultState,
87
83
  values: values ?? this.options.defaultValues ?? ((_a2 = this.options.defaultState) == null ? void 0 : _a2.values),
88
- fieldMeta
84
+ fieldMetaBase
89
85
  });
90
86
  }
91
87
  );
92
88
  };
93
89
  this.validateAllFields = async (cause) => {
94
90
  const fieldValidationPromises = [];
95
- this.store.batch(() => {
91
+ store.batch(() => {
96
92
  void Object.values(this.fieldInfo).forEach((field) => {
97
93
  if (!field.instance) return;
98
94
  const fieldInstance = field.instance;
@@ -119,7 +115,7 @@ class FormApi {
119
115
  (fieldKey) => fieldKeysToValidate.some((key) => fieldKey.startsWith(key))
120
116
  );
121
117
  const fieldValidationPromises = [];
122
- this.store.batch(() => {
118
+ store.batch(() => {
123
119
  fieldsToValidate.forEach((nestedField) => {
124
120
  fieldValidationPromises.push(
125
121
  Promise.resolve().then(() => this.validateField(nestedField, cause))
@@ -142,7 +138,7 @@ class FormApi {
142
138
  const validates = utils.getSyncValidatorArray(cause, this.options);
143
139
  let hasErrored = false;
144
140
  const fieldsErrorMap = {};
145
- this.store.batch(() => {
141
+ store.batch(() => {
146
142
  for (const validateObj of validates) {
147
143
  if (!validateObj.validate) continue;
148
144
  const rawError = this.runValidator({
@@ -177,7 +173,7 @@ class FormApi {
177
173
  }
178
174
  }
179
175
  if (this.state.errorMap[errorMapKey] !== formError) {
180
- this.store.setState((prev) => ({
176
+ this.baseStore.setState((prev) => ({
181
177
  ...prev,
182
178
  errorMap: {
183
179
  ...prev.errorMap,
@@ -192,7 +188,7 @@ class FormApi {
192
188
  });
193
189
  const submitErrKey = getErrorMapKey("submit");
194
190
  if (this.state.errorMap[submitErrKey] && cause !== "submit" && !hasErrored) {
195
- this.store.setState((prev) => ({
191
+ this.baseStore.setState((prev) => ({
196
192
  ...prev,
197
193
  errorMap: {
198
194
  ...prev.errorMap,
@@ -205,7 +201,7 @@ class FormApi {
205
201
  this.validateAsync = async (cause) => {
206
202
  const validates = utils.getAsyncValidatorArray(cause, this.options);
207
203
  if (!this.state.isFormValidating) {
208
- this.store.setState((prev) => ({ ...prev, isFormValidating: true }));
204
+ this.baseStore.setState((prev) => ({ ...prev, isFormValidating: true }));
209
205
  }
210
206
  const promises = [];
211
207
  let fieldErrors;
@@ -265,7 +261,7 @@ class FormApi {
265
261
  }
266
262
  }
267
263
  }
268
- this.store.setState((prev) => ({
264
+ this.baseStore.setState((prev) => ({
269
265
  ...prev,
270
266
  errorMap: {
271
267
  ...prev.errorMap,
@@ -296,7 +292,7 @@ class FormApi {
296
292
  }
297
293
  }
298
294
  }
299
- this.store.setState((prev) => ({
295
+ this.baseStore.setState((prev) => ({
300
296
  ...prev,
301
297
  isFormValidating: false
302
298
  }));
@@ -311,7 +307,7 @@ class FormApi {
311
307
  };
312
308
  this.handleSubmit = async () => {
313
309
  var _a2, _b, _c, _d;
314
- this.store.setState((old) => ({
310
+ this.baseStore.setState((old) => ({
315
311
  ...old,
316
312
  // Submission attempts mark the form as not submitted
317
313
  isSubmitted: false,
@@ -319,9 +315,9 @@ class FormApi {
319
315
  submissionAttempts: old.submissionAttempts + 1
320
316
  }));
321
317
  if (!this.state.canSubmit) return;
322
- this.store.setState((d) => ({ ...d, isSubmitting: true }));
318
+ this.baseStore.setState((d) => ({ ...d, isSubmitting: true }));
323
319
  const done = () => {
324
- this.store.setState((prev) => ({ ...prev, isSubmitting: false }));
320
+ this.baseStore.setState((prev) => ({ ...prev, isSubmitting: false }));
325
321
  };
326
322
  await this.validateAllFields("submit");
327
323
  if (!this.state.isValid) {
@@ -332,7 +328,7 @@ class FormApi {
332
328
  });
333
329
  return;
334
330
  }
335
- this.store.batch(() => {
331
+ store.batch(() => {
336
332
  void Object.values(this.fieldInfo).forEach((field) => {
337
333
  var _a3, _b2, _c2;
338
334
  (_c2 = (_b2 = (_a3 = field.instance) == null ? void 0 : _a3.options.listeners) == null ? void 0 : _b2.onSubmit) == null ? void 0 : _c2.call(_b2, {
@@ -343,8 +339,8 @@ class FormApi {
343
339
  });
344
340
  try {
345
341
  await ((_d = (_c = this.options).onSubmit) == null ? void 0 : _d.call(_c, { value: this.state.values, formApi: this }));
346
- this.store.batch(() => {
347
- this.store.setState((prev) => ({ ...prev, isSubmitted: true }));
342
+ store.batch(() => {
343
+ this.baseStore.setState((prev) => ({ ...prev, isSubmitted: true }));
348
344
  done();
349
345
  });
350
346
  } catch (err) {
@@ -370,12 +366,15 @@ class FormApi {
370
366
  });
371
367
  };
372
368
  this.setFieldMeta = (field, updater) => {
373
- this.store.setState((prev) => {
369
+ this.baseStore.setState((prev) => {
374
370
  return {
375
371
  ...prev,
376
- fieldMeta: {
377
- ...prev.fieldMeta,
378
- [field]: utils.functionalUpdate(updater, prev.fieldMeta[field])
372
+ fieldMetaBase: {
373
+ ...prev.fieldMetaBase,
374
+ [field]: utils.functionalUpdate(
375
+ updater,
376
+ prev.fieldMetaBase[field]
377
+ )
379
378
  }
380
379
  };
381
380
  });
@@ -400,7 +399,7 @@ class FormApi {
400
399
  };
401
400
  this.setFieldValue = (field, updater, opts2) => {
402
401
  const dontUpdateMeta = (opts2 == null ? void 0 : opts2.dontUpdateMeta) ?? false;
403
- this.store.batch(() => {
402
+ store.batch(() => {
404
403
  if (!dontUpdateMeta) {
405
404
  this.setFieldMeta(field, (prev) => ({
406
405
  ...prev,
@@ -413,7 +412,7 @@ class FormApi {
413
412
  }
414
413
  }));
415
414
  }
416
- this.store.setState((prev) => {
415
+ this.baseStore.setState((prev) => {
417
416
  return {
418
417
  ...prev,
419
418
  values: utils.setBy(prev.values, field, updater)
@@ -422,10 +421,10 @@ class FormApi {
422
421
  });
423
422
  };
424
423
  this.deleteField = (field) => {
425
- this.store.setState((prev) => {
424
+ this.baseStore.setState((prev) => {
426
425
  const newState = { ...prev };
427
426
  newState.values = utils.deleteBy(newState.values, field);
428
- delete newState.fieldMeta[field];
427
+ delete newState.fieldMetaBase[field];
429
428
  return newState;
430
429
  });
431
430
  delete this.fieldInfo[field];
@@ -514,80 +513,131 @@ class FormApi {
514
513
  this.validateField(`${field}[${index1}]`, "change");
515
514
  this.validateField(`${field}[${index2}]`, "change");
516
515
  };
517
- this.store = new store.Store(
516
+ this.baseStore = new store.Store(
518
517
  getDefaultFormState({
519
518
  ...opts == null ? void 0 : opts.defaultState,
520
519
  values: (opts == null ? void 0 : opts.defaultValues) ?? ((_a = opts == null ? void 0 : opts.defaultState) == null ? void 0 : _a.values),
521
520
  isFormValid: true
522
- }),
523
- {
524
- onUpdate: () => {
525
- var _a2, _b, _c, _d;
526
- let { state } = this.store;
527
- const fieldMetaValues = Object.values(state.fieldMeta);
528
- const isFieldsValidating = fieldMetaValues.some(
529
- (field) => field == null ? void 0 : field.isValidating
530
- );
531
- const isFieldsValid = !fieldMetaValues.some(
532
- (field) => (field == null ? void 0 : field.errorMap) && utils.isNonEmptyArray(Object.values(field.errorMap).filter(Boolean))
533
- );
534
- const isTouched = fieldMetaValues.some((field) => field == null ? void 0 : field.isTouched);
535
- const isBlurred = fieldMetaValues.some((field) => field == null ? void 0 : field.isBlurred);
536
- if (isTouched && ((_a2 = state == null ? void 0 : state.errorMap) == null ? void 0 : _a2.onMount)) {
537
- state.errorMap.onMount = void 0;
521
+ })
522
+ );
523
+ this.fieldMetaDerived = new store.Derived({
524
+ deps: [this.baseStore],
525
+ fn: ({ prevDepVals, currDepVals, prevVal: _prevVal }) => {
526
+ var _a2;
527
+ const prevVal = _prevVal;
528
+ const prevBaseStore = prevDepVals == null ? void 0 : prevDepVals[0];
529
+ const currBaseStore = currDepVals[0];
530
+ const fieldMeta = {};
531
+ for (const fieldName of Object.keys(
532
+ currBaseStore.fieldMetaBase
533
+ )) {
534
+ const currBaseVal = currBaseStore.fieldMetaBase[fieldName];
535
+ const prevBaseVal = prevBaseStore == null ? void 0 : prevBaseStore.fieldMetaBase[fieldName];
536
+ let fieldErrors = (_a2 = prevVal == null ? void 0 : prevVal[fieldName]) == null ? void 0 : _a2.errors;
537
+ if (!prevBaseVal || currBaseVal.errorMap !== prevBaseVal.errorMap) {
538
+ fieldErrors = Object.values(currBaseVal.errorMap ?? {}).filter(
539
+ (val) => val !== void 0
540
+ );
538
541
  }
539
- const isDirty = fieldMetaValues.some((field) => field == null ? void 0 : field.isDirty);
540
- const isPristine = !isDirty;
541
- const hasOnMountError = Boolean(
542
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
543
- ((_b = state.errorMap) == null ? void 0 : _b.onMount) || // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
544
- fieldMetaValues.some((f) => {
545
- var _a3;
546
- return (_a3 = f == null ? void 0 : f.errorMap) == null ? void 0 : _a3.onMount;
547
- })
548
- );
549
- const isValidating = isFieldsValidating || state.isFormValidating;
550
- state.errors = Object.values(state.errorMap).reduce((prev, curr) => {
551
- if (curr === void 0) return prev;
552
- if (typeof curr === "string") {
553
- prev.push(curr);
554
- return prev;
555
- } else if (curr && isFormValidationError(curr)) {
556
- prev.push(curr.form);
557
- return prev;
558
- }
559
- return prev;
560
- }, []);
561
- const isFormValid = state.errors.length === 0;
562
- const isValid = isFieldsValid && isFormValid;
563
- const canSubmit = state.submissionAttempts === 0 && !isTouched && !hasOnMountError || !isValidating && !state.isSubmitting && isValid;
564
- state = {
565
- ...state,
566
- isFieldsValidating,
567
- isFieldsValid,
568
- isFormValid,
569
- isValid,
570
- canSubmit,
571
- isTouched,
572
- isBlurred,
573
- isPristine,
574
- isDirty
542
+ const isFieldPristine = !currBaseVal.isDirty;
543
+ fieldMeta[fieldName] = {
544
+ ...currBaseVal,
545
+ errors: fieldErrors,
546
+ isPristine: isFieldPristine
575
547
  };
576
- this.state = state;
577
- this.store.state = this.state;
578
- const transformArray = ((_c = this.options.transform) == null ? void 0 : _c.deps) ?? [];
579
- const shouldTransform = transformArray.length !== this.prevTransformArray.length || transformArray.some((val, i) => val !== this.prevTransformArray[i]);
580
- if (shouldTransform) {
581
- (_d = this.options.transform) == null ? void 0 : _d.fn(this);
582
- this.store.state = this.state;
583
- this.prevTransformArray = transformArray;
584
- }
585
548
  }
549
+ return fieldMeta;
586
550
  }
587
- );
588
- this.state = this.store.state;
551
+ });
552
+ this.store = new store.Derived({
553
+ deps: [this.baseStore, this.fieldMetaDerived],
554
+ fn: ({ prevDepVals, currDepVals, prevVal: _prevVal }) => {
555
+ var _a2, _b, _c, _d;
556
+ const prevVal = _prevVal;
557
+ const prevBaseStore = prevDepVals == null ? void 0 : prevDepVals[0];
558
+ const currBaseStore = currDepVals[0];
559
+ const fieldMetaValues = Object.values(currBaseStore.fieldMetaBase);
560
+ const isFieldsValidating = fieldMetaValues.some(
561
+ (field) => field == null ? void 0 : field.isValidating
562
+ );
563
+ const isFieldsValid = !fieldMetaValues.some(
564
+ (field) => (field == null ? void 0 : field.errorMap) && utils.isNonEmptyArray(Object.values(field.errorMap).filter(Boolean))
565
+ );
566
+ const isTouched = fieldMetaValues.some((field) => field == null ? void 0 : field.isTouched);
567
+ const isBlurred = fieldMetaValues.some((field) => field == null ? void 0 : field.isBlurred);
568
+ const shouldInvalidateOnMount = (
569
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
570
+ isTouched && ((_a2 = currBaseStore == null ? void 0 : currBaseStore.errorMap) == null ? void 0 : _a2.onMount)
571
+ );
572
+ const isDirty = fieldMetaValues.some((field) => field == null ? void 0 : field.isDirty);
573
+ const isPristine = !isDirty;
574
+ const hasOnMountError = Boolean(
575
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
576
+ ((_b = currBaseStore.errorMap) == null ? void 0 : _b.onMount) || // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
577
+ fieldMetaValues.some((f) => {
578
+ var _a3;
579
+ return (_a3 = f == null ? void 0 : f.errorMap) == null ? void 0 : _a3.onMount;
580
+ })
581
+ );
582
+ const isValidating = !!isFieldsValidating;
583
+ let errors = (prevVal == null ? void 0 : prevVal.errors) ?? [];
584
+ if (!prevBaseStore || currBaseStore.errorMap !== prevBaseStore.errorMap) {
585
+ errors = Object.values(currBaseStore.errorMap).reduce(
586
+ (prev, curr) => {
587
+ if (curr === void 0) return prev;
588
+ if (typeof curr === "string") {
589
+ prev.push(curr);
590
+ return prev;
591
+ } else if (curr && isFormValidationError(curr)) {
592
+ prev.push(curr.form);
593
+ return prev;
594
+ }
595
+ return prev;
596
+ },
597
+ []
598
+ );
599
+ }
600
+ const isFormValid = errors.length === 0;
601
+ const isValid = isFieldsValid && isFormValid;
602
+ const canSubmit = currBaseStore.submissionAttempts === 0 && !isTouched && !hasOnMountError || !isValidating && !currBaseStore.isSubmitting && isValid;
603
+ let errorMap = currBaseStore.errorMap;
604
+ if (shouldInvalidateOnMount) {
605
+ errors = errors.filter(
606
+ (err) => err !== currBaseStore.errorMap.onMount
607
+ );
608
+ errorMap = Object.assign(errorMap, { onMount: void 0 });
609
+ }
610
+ let state = {
611
+ ...currBaseStore,
612
+ errorMap,
613
+ fieldMeta: this.fieldMetaDerived.state,
614
+ errors,
615
+ isFieldsValidating,
616
+ isFieldsValid,
617
+ isFormValid,
618
+ isValid,
619
+ canSubmit,
620
+ isTouched,
621
+ isBlurred,
622
+ isPristine,
623
+ isDirty
624
+ };
625
+ const transformArray = ((_c = this.options.transform) == null ? void 0 : _c.deps) ?? [];
626
+ const shouldTransform = transformArray.length !== this.prevTransformArray.length || transformArray.some((val, i) => val !== this.prevTransformArray[i]);
627
+ if (shouldTransform) {
628
+ const newObj = Object.assign({}, this, { state });
629
+ (_d = this.options.transform) == null ? void 0 : _d.fn(newObj);
630
+ state = newObj.state;
631
+ this.prevTransformArray = transformArray;
632
+ }
633
+ return state;
634
+ }
635
+ });
589
636
  this.update(opts || {});
590
637
  }
638
+ get state() {
639
+ return this.store.state;
640
+ }
591
641
  /**
592
642
  * @private
593
643
  */
@@ -608,7 +658,7 @@ class FormApi {
608
658
  * Updates the form's errorMap
609
659
  */
610
660
  setErrorMap(errorMap) {
611
- this.store.setState((prev) => ({
661
+ this.baseStore.setState((prev) => ({
612
662
  ...prev,
613
663
  errorMap: {
614
664
  ...prev.errorMap,