Skip to content

Commit e922e23

Browse files
committed
Pass errors to parent directly
1 parent 92b6fdc commit e922e23

File tree

2 files changed

+29
-33
lines changed

2 files changed

+29
-33
lines changed

example/src/App.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ import {
1313
FormTextArea,
1414
ChildForm,
1515
yupValidator,
16-
useArrayForm,
17-
memberCopy
16+
useArrayForm
1817
} from "typed-react-form";
1918
import { VisualRender } from "./VisualRender";
2019
import * as yup from "yup";
@@ -102,7 +101,7 @@ export function ArrayTest() {
102101
onSubmit={(ev) => {
103102
ev.preventDefault();
104103
console.log(form.values);
105-
setValues(memberCopy(form.values));
104+
setValues({ ...form.values });
106105
}}
107106
>
108107
<p>Name</p>

src/form.ts

Lines changed: 27 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export type DirtyMap<T> = {
1010
[Key in keyof T]?: boolean;
1111
};
1212

13-
export type ErrorType<T, Error> = Error | (T extends {} ? ErrorMap<T, Error> : never);
13+
export type ErrorType<T, Error> = Error | (T extends {} ? ErrorMap<NonNullable<T>, Error> : never);
1414

1515
export type ErrorMap<T, Error> = {
1616
[Key in keyof T]?: ErrorType<T[Key], Error>;
@@ -19,7 +19,7 @@ export type ErrorMap<T, Error> = {
1919
export type DefaultError = string;
2020
export type DefaultState = { isSubmitting: boolean };
2121

22-
export function memberCopy<T>(value: T): T {
22+
function memberCopy<T>(value: T): T {
2323
if (Array.isArray(value)) {
2424
return [...value] as any;
2525
} else if (typeof value === "object") {
@@ -29,6 +29,12 @@ export function memberCopy<T>(value: T): T {
2929
}
3030
}
3131

32+
function addDistinct<T extends any[]>(arr1: T, arr2: T) {
33+
for (let i = 0; i < arr2.length; i++) {
34+
if (!arr1.includes(arr2[i])) arr1.push(arr2[i]);
35+
}
36+
}
37+
3238
/**
3339
* Compares 2 objects that only contain primitive fields (no object fields)
3440
* @returns true when different, false when 'equal', undefined when an object field was found.
@@ -155,8 +161,6 @@ export class FormState<T, State = DefaultState, Error extends string = DefaultEr
155161
notifyParent: boolean = true,
156162
fireAny: boolean = true
157163
) {
158-
if (key === "author") console.trace("setValueInternal", key, value, dirty);
159-
160164
let valueMap = isDefault ? this.defaultValues : this.values;
161165
if (value === undefined) {
162166
if (Array.isArray(valueMap)) valueMap.splice(key as number, 1);
@@ -312,11 +316,10 @@ export class FormState<T, State = DefaultState, Error extends string = DefaultEr
312316
* @param error The error.
313317
* @param notifyChild Should this form notify the child form about this change?
314318
* @param notifyParent Should this form notify the parent form about this change?
315-
* @param setValuesWasUsed
316319
*/
317320
public setError<Key extends keyof T>(
318321
key: Key,
319-
error: ErrorType<T[Key], Error> | undefined,
322+
error: ErrorType<NonNullable<T[Key]>, Error> | undefined,
320323
notifyChild: boolean = true,
321324
notifyParent: boolean = true,
322325
fireAny: boolean = true
@@ -327,17 +330,16 @@ export class FormState<T, State = DefaultState, Error extends string = DefaultEr
327330
else this.errorMap[key] = error;
328331

329332
if (notifyChild && this.childMap[key]) {
330-
if (typeof error === "object") {
331-
let changed = this.childMap[key]!.setErrors((error as any) ?? {}, true, false);
332-
if (!changed) return false;
333-
} else {
334-
this.childMap[key]!.setErrors({} as any, true, false);
335-
}
333+
let changed = this.childMap[key]!.setErrors(error ?? ({} as any), true, false);
334+
if (!changed && error !== undefined) return false;
336335
}
337336

338337
this.fireListeners(key);
339338
if (fireAny) this.fireAnyListeners(); // When setValuesWasUsed, it will call fireAnyListener itself when all values were set
340-
if (notifyParent) this.updateParentErrors(); // Will call setError on parent
339+
340+
if (notifyParent && this instanceof ChildFormState) {
341+
this.parent.setError(this.name, this.error ? memberCopy(this.errorMap) : undefined, false, true);
342+
}
341343
return true;
342344
}
343345

@@ -348,34 +350,37 @@ export class FormState<T, State = DefaultState, Error extends string = DefaultEr
348350
* @param notifyParent Should this form notify the parent form about this change?
349351
*/
350352
public setErrors(errors: ErrorType<T, Error>, notifyChild: boolean = true, notifyParent: boolean = true) {
353+
let keys = Object.keys(this.errorMap);
354+
351355
if (typeof errors === "string") {
352356
if (notifyParent && this instanceof ChildFormState) {
353357
this.parent.setError(this.name, errors, false, true);
354358
}
355-
return;
356-
}
357-
let keys = Object.keys(this.errorMap);
358-
let newKeys = Object.keys(errors);
359-
for (let i = 0; i < newKeys.length; i++) {
360-
if (!keys.includes(newKeys[i])) keys.push(newKeys[i]);
359+
errors = {} as any;
360+
} else {
361+
addDistinct(keys, Object.keys(errors));
361362
}
363+
362364
let changed = false;
363365
for (let i = keys.length; i >= 0; i--) {
364366
let key = keys[i] as keyof T;
365367
if (
366368
this.setError(
367369
key,
368-
errors[key] as any,
370+
(errors as ErrorMap<T, Error>)[key] as ErrorType<NonNullable<T[keyof T]>, Error>,
369371
notifyChild,
370-
false, // Will call updateParentErrors by itself after all values have been copied, see 3 lines down
372+
false, // Will call this.parent.setError by itself after all values have been copied, see 3 lines down
371373
false // Will call fireAnyListener by itself after all values have been copied, see 3 lines down
372374
)
373375
)
374376
changed = true;
375377
}
376378
if (!changed) return false;
379+
377380
this.fireAnyListeners();
378-
if (notifyParent) this.updateParentErrors();
381+
if (notifyParent && this instanceof ChildFormState) {
382+
this.parent.setError(this.name, this.error ? (memberCopy(this.errorMap) as any) : undefined, false, true);
383+
}
379384
return true;
380385
}
381386

@@ -512,10 +517,6 @@ export class FormState<T, State = DefaultState, Error extends string = DefaultEr
512517
// Not implemented for root form, as it does not have a parent
513518
}
514519

515-
protected updateParentErrors() {
516-
// Not implemented for root form, as it does not have a parent
517-
}
518-
519520
protected updateParentState() {
520521
// Not implemented for root form, as it does not have a parent
521522
}
@@ -556,10 +557,6 @@ export class ChildFormState<Parent, Key extends keyof Parent, ParentState, Paren
556557
);
557558
}
558559

559-
protected updateParentErrors() {
560-
this.parent.setError(this.name, this.error ? (memberCopy(this.errorMap) as any) : undefined, false, true);
561-
}
562-
563560
protected updateParentState() {
564561
this.parent.setState(memberCopy(this.state), false, true);
565562
}

0 commit comments

Comments
 (0)