Skip to content

Commit aaff1c0

Browse files
committed
Add several fixes to manager api
- fix falsy initialValues - updates error and validation on field unregistering - do not trigger renders on field when form is being rendered - return copy of values/registered fields to make it immutable - do not count a field that is being silently registered as registered until it's finished
1 parent 955aea0 commit aaff1c0

File tree

2 files changed

+60
-17
lines changed

2 files changed

+60
-17
lines changed

packages/form-state-manager/src/types/manager-api.d.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export type HandleSubmit = (event?: FormEvent) => void;
2424
export type RegisterField = (field: FieldConfig) => void;
2525
export type UnregisterField = (field: Omit<FieldConfig, 'render'>) => void;
2626
export type GetState = () => ManagerState;
27-
export type OnSubmit = (values: AnyObject) => any;
27+
export type OnSubmit = (values: AnyObject, formApi: ManagerState, event?: FormEvent) => any;
2828
export type GetFieldValue = (name: string) => any;
2929
export type GetFieldState = (name: string) => ExtendedFieldState | undefined;
3030
export type Focus = (name: string) => void;
@@ -33,7 +33,7 @@ export type UpdateValid = (valid: boolean) => void;
3333
export type UpdateError = (name: string, error: string | undefined) => void;
3434
export type Batch = (callback: Callback) => void;
3535
export type Render = () => void;
36-
export type Subscribe = (subscriberConfig: SubscriberConfig) => void;
36+
export type Subscribe = (subscriberConfig: SubscriberConfig, isField?: boolean, isForm?: boolean) => void;
3737
export type Unsubscribe = (subscriberConfig: Omit<SubscriberConfig, 'render'>) => void;
3838
export type Reset = (initialValues?: AnyObject) => void;
3939
export type Restart = () => void;
@@ -85,6 +85,7 @@ export interface FieldListener {
8585
asyncWatcher: AsyncWatcherApi;
8686
fields: FieldListenerFields;
8787
validateFields?: Array<string>;
88+
isForm?: boolean;
8889
}
8990

9091
export interface FieldListeners {

packages/form-state-manager/src/utils/manager-api.ts

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ import CreateManagerApi, {
1919
ManagerApiFunctions,
2020
ExtendedFieldState,
2121
InitilizeInputFunction,
22-
CreateManagerApiConfig
22+
CreateManagerApiConfig,
23+
FieldListener
2324
} from '../types/manager-api';
2425
import AnyObject from '../types/any-object';
2526
import FieldConfig, { IsEqual } from '../types/field-config';
@@ -33,7 +34,8 @@ import focusError from './focus-error';
3334

3435
export const defaultIsEqual = (a: any, b: any) => a === b;
3536

36-
const isLast = (fieldListeners: AnyObject, name: string) => fieldListeners?.[name]?.count === 1;
37+
const isLast = (fieldListeners: AnyObject, name: string, registeringFields: string[]) =>
38+
fieldListeners?.[name]?.count - registeringFields.filter((field) => field === name).length === 1;
3739

3840
const noState = (fieldListeners: AnyObject, name: string) => !fieldListeners?.[name]?.state;
3941

@@ -256,6 +258,7 @@ const createManagerApi: CreateManagerApi = ({
256258
let runningValidators = 0;
257259
let flatSubmitErrors: AnyObject = {};
258260
let flatErrors: AnyObject = {};
261+
const registeringFields: string[] = [];
259262

260263
function updateRunningValidators(increment: number): void {
261264
runningValidators = Math.max(runningValidators + increment, 0);
@@ -610,7 +613,10 @@ const createManagerApi: CreateManagerApi = ({
610613
}
611614

612615
function handleSubmit(event?: FormEvent): void {
613-
event && event.preventDefault && event.preventDefault();
616+
if (event) {
617+
typeof event.preventDefault && typeof event.preventDefault === 'function' && event.preventDefault();
618+
typeof event.stopPropagation && typeof event.stopPropagation === 'function' && event.stopPropagation();
619+
}
614620

615621
if (state.submitting) {
616622
return;
@@ -643,7 +649,7 @@ const createManagerApi: CreateManagerApi = ({
643649
return;
644650
}
645651

646-
const result = config.onSubmit(state.values);
652+
const result = config.onSubmit({ ...state.values }, { ...state, values: { ...state.values } }, event);
647653

648654
if (isPromise(result)) {
649655
setSubmitting();
@@ -747,6 +753,7 @@ const createManagerApi: CreateManagerApi = ({
747753
function registerField(field: FieldConfig): void {
748754
isSilent = field.silent ? isSilent + 1 : isSilent;
749755
registeringField = field.internalId || field.name;
756+
field.silent && registeringFields.push(field.name);
750757
batch(() => {
751758
const render = prepareRerender();
752759
addIfUnique(state.registeredFields, field.name);
@@ -755,7 +762,11 @@ const createManagerApi: CreateManagerApi = ({
755762
shouldExecute(config.initializeOnMount, field.initializeOnMount) ||
756763
(!isInitialized(field.name) && typeof field.initialValue !== 'undefined')
757764
) {
758-
set(state.values, field.name, field.initialValue || get(state.initialValues, field.name));
765+
set(
766+
state.values,
767+
field.name,
768+
Object.prototype.hasOwnProperty.call(field, 'initialValue') ? field.initialValue : get(state.initialValues, field.name)
769+
);
759770
}
760771

761772
let setDirty = false;
@@ -815,6 +826,8 @@ const createManagerApi: CreateManagerApi = ({
815826
rerender(silentRender);
816827
silentRender = [];
817828
registeringField = undefined;
829+
830+
registeringFields.splice(registeringFields.indexOf(field.name), 1);
818831
}
819832
}
820833

@@ -823,15 +836,23 @@ const createManagerApi: CreateManagerApi = ({
823836
const render = prepareRerender();
824837
delete state.fieldListeners[field.name].fields[field.internalId];
825838

826-
if (isLast(state.fieldListeners, field.name)) {
839+
if (isLast(state.fieldListeners, field.name, registeringFields)) {
827840
state.registeredFields = state.registeredFields.filter((fieldName: string) => fieldName !== field.name);
828841
if (shouldExecute(config.clearOnUnmount || config.destroyOnUnregister, field.clearOnUnmount)) {
829842
set(state.values, field.name, field.value);
830843
}
844+
845+
updateError(field.name);
831846
}
832847

833848
unsubscribe(field as SubscriberConfig);
834849

850+
revalidateFields(state.registeredFields);
851+
852+
if (config.validate) {
853+
validateForm(config.validate);
854+
}
855+
835856
render();
836857
});
837858
}
@@ -861,7 +882,7 @@ const createManagerApi: CreateManagerApi = ({
861882
}
862883

863884
function getState(): ManagerState {
864-
return { ...state };
885+
return { ...state, values: { ...state.values } };
865886
}
866887

867888
function updateValidating(validating: boolean) {
@@ -918,17 +939,34 @@ const createManagerApi: CreateManagerApi = ({
918939
subscribeTo && subscribeTo.forEach((to) => addIfUnique(batched, to));
919940
shouldRerender = true;
920941
} else {
942+
if (config.subscription) {
943+
let refreshForm: boolean | undefined = false;
944+
945+
traverseObject(config.subscription, (subscribed, key) => {
946+
if (!refreshForm) {
947+
refreshForm = subscribed && (key === 'all' || subscribeTo?.includes(key));
948+
}
949+
});
950+
951+
if (refreshForm) {
952+
const formField = Object.values(state.fieldListeners)?.find((fieldListener: FieldListener) => fieldListener.isForm)?.fields[0];
953+
954+
if (formField) {
955+
formField.render();
956+
return;
957+
}
958+
}
959+
}
960+
921961
traverseObject(state.fieldListeners, (fieldListener) => {
922962
traverseObject(fieldListener.fields, (field, key) => {
923963
if (String(registeringField) !== String(key)) {
924964
let shouldRender: boolean | undefined = false;
925965

926-
const mergedSubscription = { ...config.subscription, ...field.subscription };
927-
928966
if (!config.subscription && !field.subscription) {
929967
shouldRender = true;
930-
} else {
931-
traverseObject(mergedSubscription, (subscribed, key) => {
968+
} else if (field.subscription) {
969+
traverseObject(field.subscription, (subscribed, key) => {
932970
if (!shouldRender) {
933971
shouldRender = subscribed && (key === 'all' || subscribeTo?.includes(key));
934972
}
@@ -955,7 +993,7 @@ const createManagerApi: CreateManagerApi = ({
955993
}
956994
}
957995

958-
function subscribe(subscriberConfig: SubscriberConfig, isField?: boolean): void {
996+
function subscribe(subscriberConfig: SubscriberConfig, isField?: boolean, isForm?: boolean): void {
959997
state.fieldListeners[subscriberConfig.name] = {
960998
...state.fieldListeners[subscriberConfig.name],
961999
...(isField
@@ -977,12 +1015,16 @@ const createManagerApi: CreateManagerApi = ({
9771015
beforeSubmit: subscriberConfig.beforeSubmit,
9781016
isEqual: subscriberConfig.isEqual
9791017
}
980-
}
1018+
},
1019+
...(isForm && { isForm: true })
9811020
};
9821021
}
9831022

9841023
function unsubscribe(subscriberConfig: Omit<SubscriberConfig, 'render'>): void {
985-
if (isLast(state.fieldListeners, String(subscriberConfig.name)) && noState(state.fieldListeners, String(subscriberConfig.name))) {
1024+
if (
1025+
isLast(state.fieldListeners, String(subscriberConfig.name), registeringFields) &&
1026+
noState(state.fieldListeners, String(subscriberConfig.name))
1027+
) {
9861028
delete state.fieldListeners[subscriberConfig.name];
9871029
} else {
9881030
state.fieldListeners[subscriberConfig.name].count = state.fieldListeners[subscriberConfig.name].count - 1;
@@ -1016,7 +1058,7 @@ const createManagerApi: CreateManagerApi = ({
10161058
}
10171059

10181060
function getRegisteredFields(): Array<string> {
1019-
return state.registeredFields;
1061+
return [...state.registeredFields];
10201062
}
10211063

10221064
return managerApi;

0 commit comments

Comments
 (0)