Skip to content

Commit f63142c

Browse files
committed
Allow to change configration for registered field
1 parent aaff1c0 commit f63142c

File tree

6 files changed

+114
-9
lines changed

6 files changed

+114
-9
lines changed

packages/form-state-manager/src/files/form-manager-context.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ const FormManagerContext = createContext<ManagerContextValue>({
3939
shift: noop,
4040
swap: noop,
4141
unshift: noop,
42-
update: noop
42+
update: noop,
43+
updateFieldConfig: noop
4344
});
4445

4546
export default FormManagerContext;

packages/form-state-manager/src/files/use-field.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useContext, useReducer, useState } from 'react';
1+
import { useEffect, useContext, useReducer, useState, useRef } from 'react';
22
import FormManagerContext from '../files/form-manager-context';
33
import UseField, { OnChangeEvent, UseFieldData, Format } from '../types/use-field';
44
import isEmpty from 'lodash/isEmpty';
@@ -78,8 +78,11 @@ const useField = ({
7878
allowNull,
7979
...props
8080
}: UseField): UseFieldData => {
81-
const { registerField, unregisterField, change, getFieldValue, blur, focus, formOptions, ...rest } = useContext(FormManagerContext);
81+
const { registerField, unregisterField, change, getFieldValue, blur, focus, formOptions, updateFieldConfig, ...rest } = useContext(
82+
FormManagerContext
83+
);
8284
const [, render] = useReducer((count) => count + 1, 0);
85+
const mounted = useRef(false);
8386
const [id] = useState(() => {
8487
const internalId = generateId();
8588

@@ -131,6 +134,23 @@ const useField = ({
131134
change(name, parse(sanitizedValue, name));
132135
};
133136

137+
const validateToCheck = validate ? JSON.stringify(validate) : false;
138+
139+
useEffect(() => {
140+
if (mounted.current) {
141+
updateFieldConfig({
142+
name,
143+
internalId: id,
144+
...(Object.prototype.hasOwnProperty.call(props, 'initialValue') && {
145+
initialValue: dataType ? convertValue(props.initialValue, dataType) : props.initialValue
146+
}),
147+
defaultValue: dataType ? convertValue(defaultValue, dataType) : defaultValue,
148+
validate,
149+
initializeOnMount
150+
});
151+
}
152+
}, [dataType, validateToCheck, props.initialValue, defaultValue]); // eslint-disable-line react-hooks/exhaustive-deps
153+
134154
useEffect(
135155
() => {
136156
formOptions.afterSilentRegistration({ name, internalId: id });
@@ -139,6 +159,8 @@ const useField = ({
139159
formOptions.registerInputFile(name);
140160
}
141161

162+
mounted.current = true;
163+
142164
return () => {
143165
if (type === 'file') {
144166
formOptions.unregisterInputFile(name);

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { FormEvent } from 'react';
22
import AnyObject from './any-object';
3-
import { Batch, Change, ManagerState, GetState, Blur, Focus, Unsubscribe, Subscribe } from './manager-api';
3+
import { Batch, Change, ManagerState, GetState, Blur, Focus, Unsubscribe, Subscribe, UpdateFieldConfig } from './manager-api';
44
import FieldConfig from './field-config';
55
import FieldArrayApi from './use-field-array-api';
66

@@ -24,6 +24,7 @@ export interface ManagerContextValue extends FieldArrayApi {
2424
initialValues?: AnyObject;
2525
subscribe: Subscribe;
2626
unsubscribe: Unsubscribe;
27+
updateFieldConfig: UpdateFieldConfig;
2728
}
2829

2930
export interface ManagerContext {

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export type AfterSilentRegistration = (field: Omit<FieldConfig, 'render'>) => vo
4848
export type RegisterInputFile = (name: string) => void;
4949
export type UnregisterInputFile = (name: string) => void;
5050
export type GetRegisteredFields = () => Array<string>;
51+
export type UpdateFieldConfig = (field: UpdatedConfig) => void;
5152
export interface AsyncWatcherRecord {
5253
[key: number]: Promise<unknown>;
5354
}
@@ -122,7 +123,8 @@ export type ManagerApiFunctions =
122123
| 'afterSilentRegistration'
123124
| 'registerInputFile'
124125
| 'unregisterInputFile'
125-
| 'getRegisteredFields';
126+
| 'getRegisteredFields'
127+
| 'updateFieldConfig';
126128

127129
export interface ManagerState {
128130
values: AnyObject;
@@ -156,6 +158,7 @@ export interface ManagerState {
156158
setConfig: SetConfig;
157159
afterSilentRegistration: AfterSilentRegistration;
158160
getRegisteredFields: GetRegisteredFields;
161+
updateFieldConfig: UpdateFieldConfig;
159162
registeredFields: Array<string>;
160163
fieldListeners: FieldListeners;
161164
active: string | undefined;
@@ -201,6 +204,15 @@ export interface SubscriberConfig extends AnyObject {
201204
beforeSubmit?: BeforeSubmit;
202205
}
203206

207+
export interface UpdatedConfig {
208+
validate?: Validator;
209+
initialValue?: any;
210+
defaultValue?: any;
211+
name: string;
212+
internalId: number | string;
213+
initializeOnMount?: boolean;
214+
}
215+
204216
export interface CreateManagerApiConfig {
205217
onSubmit: OnSubmit;
206218
clearOnUnmount?: boolean;

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

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ import CreateManagerApi, {
2020
ExtendedFieldState,
2121
InitilizeInputFunction,
2222
CreateManagerApiConfig,
23-
FieldListener
23+
FieldListener,
24+
UpdatedConfig
2425
} from '../types/manager-api';
2526
import AnyObject from '../types/any-object';
2627
import FieldConfig, { IsEqual } from '../types/field-config';
@@ -244,6 +245,7 @@ const createManagerApi: CreateManagerApi = ({
244245
registerInputFile,
245246
unregisterInputFile,
246247
getRegisteredFields,
248+
updateFieldConfig,
247249
...initialFormState(initialValues)
248250
};
249251
let inBatch = 0;
@@ -389,6 +391,12 @@ const createManagerApi: CreateManagerApi = ({
389391

390392
state.registeredFields.forEach(resetFieldState);
391393

394+
revalidateFields(state.registeredFields);
395+
396+
if (config.validate) {
397+
validateForm(config.validate);
398+
}
399+
392400
render();
393401
});
394402
}
@@ -1061,6 +1069,60 @@ const createManagerApi: CreateManagerApi = ({
10611069
return [...state.registeredFields];
10621070
}
10631071

1072+
function updateFieldConfig(field: UpdatedConfig): void {
1073+
const { name, internalId, validate } = field;
1074+
1075+
state.fieldListeners[name].fields[internalId] = {
1076+
...state.fieldListeners[name].fields[internalId],
1077+
validate
1078+
};
1079+
1080+
const render = prepareRerender();
1081+
1082+
if (
1083+
shouldExecute(config.initializeOnMount, field.initializeOnMount) ||
1084+
(!isInitialized(field.name) && typeof field.initialValue !== 'undefined')
1085+
) {
1086+
set(
1087+
state.values,
1088+
field.name,
1089+
Object.prototype.hasOwnProperty.call(field, 'initialValue') ? field.initialValue : get(state.initialValues, field.name)
1090+
);
1091+
}
1092+
1093+
let setDirty = false;
1094+
if (!isInitialized(field.name) && typeof field.defaultValue !== 'undefined' && typeof get(state.values, field.name) === 'undefined') {
1095+
set(state.values, field.name, field.defaultValue);
1096+
setDirty = true;
1097+
}
1098+
1099+
if (setDirty) {
1100+
state.pristine = false;
1101+
state.dirty = true;
1102+
state.dirtyFields[field.name] = true;
1103+
state.fieldListeners[field.name].state.meta.dirty = true;
1104+
state.fieldListeners[field.name].state.meta.pristine = false;
1105+
}
1106+
1107+
setFieldState(name, (prev: FieldState) => ({
1108+
...prev,
1109+
value: get(state.values, name),
1110+
...(Object.prototype.hasOwnProperty.call(field, 'initialValue') && {
1111+
meta: {
1112+
...prev.meta,
1113+
initial: field.initialValue
1114+
}
1115+
})
1116+
}));
1117+
1118+
revalidateFields([field.name, ...(state.fieldListeners[field.name]?.validateFields || state.registeredFields.filter((n) => n !== field.name))]);
1119+
if (config.validate) {
1120+
validateForm(config.validate);
1121+
}
1122+
1123+
render();
1124+
}
1125+
10641126
return managerApi;
10651127
};
10661128

packages/react-form-renderer/src/tests/form-renderer/use-field-api.test.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,12 +148,19 @@ describe('useFieldApi', () => {
148148
expect(wrapper.find(Catcher).props().arrayValidator).toEqual(expect.any(Function));
149149
});
150150

151-
it('reloads initial value', () => {
152-
const wrapper = mount(<WrapperComponent {...initialProps} />);
151+
it('reloads initial value', async () => {
152+
let wrapper;
153+
154+
await act(async () => {
155+
wrapper = mount(<WrapperComponent {...initialProps} />);
156+
});
157+
wrapper.update();
153158

154159
expect(wrapper.find(Catcher).props().meta.initial).toEqual(undefined);
155160

156-
wrapper.setProps({ initialValue: 'pepa' });
161+
await act(async () => {
162+
wrapper.setProps({ initialValue: 'pepa' });
163+
});
157164
wrapper.update();
158165

159166
expect(wrapper.find(Catcher).props().meta.initial).toEqual('pepa');

0 commit comments

Comments
 (0)