From 64418d517fc58e8a4ed36185ebd872cdd68f7c2c Mon Sep 17 00:00:00 2001 From: Nikita Zolotykh Date: Mon, 15 Jan 2024 17:56:50 +0300 Subject: [PATCH] fix: fix mutators logic --- .../components/Form/Controller/Controller.tsx | 8 +- .../core/components/Form/Controller/types.ts | 12 +-- .../core/components/Form/Controller/utils.tsx | 102 +++++++++++------- src/lib/core/components/Form/DynamicField.tsx | 6 +- .../core/components/Form/hooks/useMutators.ts | 66 +++++++++--- src/lib/core/components/Form/types/context.ts | 3 +- .../core/components/Form/types/mutators.ts | 6 ++ .../components/Inputs/TextArea/TextArea.tsx | 2 +- 8 files changed, 136 insertions(+), 69 deletions(-) diff --git a/src/lib/core/components/Form/Controller/Controller.tsx b/src/lib/core/components/Form/Controller/Controller.tsx index 7faf9bfe..67062a88 100644 --- a/src/lib/core/components/Form/Controller/Controller.tsx +++ b/src/lib/core/components/Form/Controller/Controller.tsx @@ -34,14 +34,14 @@ export const Controller = < parentOnChange, parentOnUnmount, }: ControllerProps) => { - const {config, tools, mutators, __mirror} = useDynamicFormsCtx(); + const {config, tools, mutatorsStore, __mirror} = useDynamicFormsCtx(); const firstRenderRef = React.useRef(true); const [store, setStore] = React.useState>( initializeStore({ name, spec: _spec, - mutators, + mutatorsStore, config, valueFromParent, tools, @@ -148,7 +148,7 @@ export const Controller = < name, parentOnChange, parentOnUnmount, - mutators, + mutatorsStore, config, tools, methodOnChange: fieldMethods.onChange, @@ -160,7 +160,7 @@ export const Controller = < name, parentOnChange, parentOnUnmount, - mutators, + mutatorsStore, config, tools.onChange, tools.onUnmount, diff --git a/src/lib/core/components/Form/Controller/types.ts b/src/lib/core/components/Form/Controller/types.ts index fc496f8c..92ea5c07 100644 --- a/src/lib/core/components/Form/Controller/types.ts +++ b/src/lib/core/components/Form/Controller/types.ts @@ -1,7 +1,7 @@ import {FormValue, Spec} from '../../../types'; import { DynamicFormConfig, - DynamicFormMutators, + DynamicFormMutatorsStore, DynamicFormsContext, FieldRenderProps, FieldValue, @@ -14,7 +14,7 @@ import { export interface GetSpecParams { name: string; spec: SpecType; - mutators: DynamicFormMutators; + mutatorsStore: DynamicFormMutatorsStore; } export interface GetComponentsParams { @@ -49,7 +49,7 @@ export interface GetFieldInitialsParams< valueFromParent: DirtyValue; initialValue: DirtyValue; validate: (value?: Value) => ValidateError; - mutators: DynamicFormMutators; + mutatorsStore: DynamicFormMutatorsStore; } export type FieldMethod< @@ -88,7 +88,7 @@ export interface GetFieldMethodsReturn< export interface InitializeStoreParams { name: string; spec: SpecType; - mutators: DynamicFormMutators; + mutatorsStore: DynamicFormMutatorsStore; config: DynamicFormConfig; valueFromParent: DirtyValue; tools: DynamicFormsContext['tools']; @@ -112,7 +112,7 @@ export interface ControllerStore< initialSpec: SpecType; config: DynamicFormConfig; tools: DynamicFormsContext['tools']; - mutators: DynamicFormMutators; + mutatorsStore: DynamicFormMutatorsStore; render: (props: FieldRenderProps) => JSX.Element | null; validate: (value?: Value) => ValidateError; parentOnChange: @@ -157,7 +157,7 @@ export interface UpdateStoreParams< ) => void) | null; parentOnUnmount: ((childName: string) => void) | null; - mutators: DynamicFormMutators; + mutatorsStore: DynamicFormMutatorsStore; valueFromParent: DirtyValue; config: DynamicFormConfig; tools: DynamicFormsContext['tools']; diff --git a/src/lib/core/components/Form/Controller/utils.tsx b/src/lib/core/components/Form/Controller/utils.tsx index e83a6695..0eae0311 100644 --- a/src/lib/core/components/Form/Controller/utils.tsx +++ b/src/lib/core/components/Form/Controller/utils.tsx @@ -8,6 +8,7 @@ import {isArraySpec, isCorrectSpec, isNumberSpec, isObjectSpec} from '../../../h import {FormValue, Spec} from '../../../types'; import {EMPTY_MUTATOR, OBJECT_ARRAY_CNT, OBJECT_ARRAY_FLAG} from '../constants'; import { + BaseValidateError, FieldArrayValue, FieldObjectValue, FieldRenderProps, @@ -30,13 +31,20 @@ import { UpdateStoreParams, } from './types'; -const isErrorMutatorCorrect = (errorMutator: ValidateError) => - errorMutator !== EMPTY_MUTATOR && (_.isString(errorMutator) || _.isBoolean(errorMutator)); +const isErrorMutatorCorrect = (errorMutator: {value: ValidateError} | typeof EMPTY_MUTATOR) => + errorMutator !== EMPTY_MUTATOR && + (_.isString(errorMutator.value) || + _.isBoolean(errorMutator.value) || + _.isUndefined(errorMutator.value)); -const isValueMutatorCorrect = (valueMutator: FormValue, spec: Spec) => +const isValueMutatorCorrect = ( + valueMutator: {value: FormValue} | typeof EMPTY_MUTATOR, + spec: Spec, +) => valueMutator !== EMPTY_MUTATOR && - (typeof valueMutator === spec.type || - (_.isArray(valueMutator) && spec.type === SpecTypes.Array)); + (typeof valueMutator.value === spec.type || + (_.isArray(valueMutator.value) && spec.type === SpecTypes.Array) || + valueMutator.value === undefined); export const updateParentStore = < DirtyValue extends FieldValue, @@ -68,12 +76,12 @@ export const callUnmout = < export const getSpec = ({ name, spec, - mutators, + mutatorsStore, }: GetSpecParams): SpecType => { - const mutator = _.get(mutators.spec, name, EMPTY_MUTATOR); + const mutator = _.get(mutatorsStore.spec, name, EMPTY_MUTATOR); if (mutator !== EMPTY_MUTATOR) { - const mutatedSpec = _.merge(_.cloneDeep(spec), mutator); + const mutatedSpec = _.merge(_.cloneDeep(spec), mutator.value); if (isCorrectSpec(mutatedSpec)) { return mutatedSpec; @@ -196,15 +204,15 @@ export const getFieldInitials = < valueFromParent, initialValue, validate, - mutators, + mutatorsStore, }: GetFieldInitialsParams) => { - const valueMutator = transformArrIn(_.get(mutators.values, name, EMPTY_MUTATOR)) as - | DirtyValue + const valueMutator = transformArrIn(_.get(mutatorsStore.values, name, EMPTY_MUTATOR)) as + | {value: DirtyValue} | typeof EMPTY_MUTATOR; let value = _.cloneDeep(valueFromParent); if (isValueMutatorCorrect(valueMutator, spec)) { - value = valueMutator as DirtyValue; + value = (valueMutator as {value: DirtyValue}).value; } if (_.isNil(value)) { @@ -222,13 +230,18 @@ export const getFieldInitials = < } } - let errorMutator: ValidateError = _.get(mutators.errors, name, EMPTY_MUTATOR); + let errorMutator: {value: BaseValidateError} | typeof EMPTY_MUTATOR = _.get( + mutatorsStore.errors, + name, + EMPTY_MUTATOR, + ); if (!isErrorMutatorCorrect(errorMutator)) { - errorMutator = undefined; + errorMutator = {value: undefined}; } - const error = validate?.(transformArrOut(value)) || errorMutator; + const error = + validate?.(transformArrOut(value)) || (errorMutator as {value: BaseValidateError})?.value; const dirty = !_.isEqual(value, initialValue); return { @@ -425,14 +438,14 @@ export const initializeStore = < >({ name, spec: _spec, - mutators, + mutatorsStore, config, valueFromParent, tools, parentOnChange, parentOnUnmount, }: InitializeStoreParams): ControllerStore => { - const spec = getSpec({name, spec: _spec, mutators}); + const spec = getSpec({name, spec: _spec, mutatorsStore}); const components = getComponents({spec, config}); const render = getRender({name, spec, ...components}); const validate = getValidate({spec, config}); @@ -441,7 +454,7 @@ export const initializeStore = < spec, valueFromParent, validate, - mutators, + mutatorsStore, initialValue: _.get(tools.initialValue, name), }); @@ -451,7 +464,7 @@ export const initializeStore = < initialSpec: _spec, config, tools, - mutators, + mutatorsStore, render, validate, parentOnChange, @@ -477,26 +490,26 @@ export const updateStore = < name, parentOnChange, parentOnUnmount, - mutators, + mutatorsStore, valueFromParent, config, tools, methodOnChange, }: UpdateStoreParams) => { - const storeSpecMutator = _.get(store.mutators.spec, store.name, EMPTY_MUTATOR); - const storeValueMutator = _.get(store.mutators.values, store.name, EMPTY_MUTATOR) as - | DirtyValue + const storeSpecMutator = _.get(store.mutatorsStore.spec, store.name, EMPTY_MUTATOR); + const storeValueMutator = _.get(store.mutatorsStore.values, store.name, EMPTY_MUTATOR) as + | {value: DirtyValue} | typeof EMPTY_MUTATOR; - const storeErrorMutator = _.get(store.mutators.errors, store.name, EMPTY_MUTATOR); + const storeErrorMutator = _.get(store.mutatorsStore.errors, store.name, EMPTY_MUTATOR); - const specMutator = _.get(mutators.spec, name, EMPTY_MUTATOR); - const valueMutator = _.get(mutators.values, name, EMPTY_MUTATOR) as - | DirtyValue + const specMutator = _.get(mutatorsStore.spec, name, EMPTY_MUTATOR); + const valueMutator = _.get(mutatorsStore.values, name, EMPTY_MUTATOR) as + | {value: DirtyValue} | typeof EMPTY_MUTATOR; - const errorMutator = _.get(mutators.errors, name, EMPTY_MUTATOR); + const errorMutator = _.get(mutatorsStore.errors, name, EMPTY_MUTATOR); const valueMutatorUpdated = - isValueMutatorCorrect(valueMutator, getSpec({name, spec: _spec, mutators})) && + isValueMutatorCorrect(valueMutator, getSpec({name, spec: _spec, mutatorsStore})) && valueMutator !== storeValueMutator; const errorMutatorUpdated = isErrorMutatorCorrect(errorMutator) && errorMutator !== storeErrorMutator; @@ -519,7 +532,7 @@ export const updateStore = < initializeStore({ name, spec: _spec, - mutators, + mutatorsStore, config, valueFromParent, tools, @@ -532,7 +545,7 @@ export const updateStore = < ...initializeStore({ name, spec: _spec, - mutators, + mutatorsStore, config, valueFromParent, tools, @@ -545,8 +558,10 @@ export const updateStore = < if (updateState) { nextStore = methodOnChange(nextStore, { valOrSetter: (value) => - valueMutatorUpdated ? (valueMutator as DirtyValue) : value, - ...(errorMutatorUpdated ? {errorMutator} : {}), + valueMutatorUpdated ? (valueMutator as {value: DirtyValue}).value : value, + ...(errorMutatorUpdated + ? {errorMutator: (errorMutator as {value: BaseValidateError}).value} + : {}), }); } @@ -562,19 +577,26 @@ export const updateStore = < if (updateState) { nextStore = methodOnChange(nextStore, { valOrSetter: (value) => - valueMutatorUpdated ? (valueMutator as DirtyValue) : value, - ...(errorMutatorUpdated ? {errorMutator} : {}), + valueMutatorUpdated ? (valueMutator as {value: DirtyValue}).value : value, + ...(errorMutatorUpdated + ? {errorMutator: (errorMutator as {value: BaseValidateError}).value} + : {}), }); } setStore(nextStore); } else if (updateState) { setStore( - methodOnChange(store, { - valOrSetter: (value) => - valueMutatorUpdated ? (valueMutator as DirtyValue) : value, - ...(errorMutatorUpdated ? {errorMutator} : {}), - }), + methodOnChange( + {...store, mutatorsStore}, + { + valOrSetter: (value) => + valueMutatorUpdated ? (valueMutator as {value: DirtyValue}).value : value, + ...(errorMutatorUpdated + ? {errorMutator: (errorMutator as {value: BaseValidateError}).value} + : {}), + }, + ), ); } }; diff --git a/src/lib/core/components/Form/DynamicField.tsx b/src/lib/core/components/Form/DynamicField.tsx index 6233ffcf..296ad092 100644 --- a/src/lib/core/components/Form/DynamicField.tsx +++ b/src/lib/core/components/Form/DynamicField.tsx @@ -47,7 +47,7 @@ export const DynamicField: React.FC = ({ const SearchContext = useCreateSearchContext(); const {tools, store} = useStore(name); const watcher = useIntegrationFF(store, withoutInsertFFDebounce); - const {mutators, mutateDFState} = useMutators(externalMutators); + const {mutatorsStore, mutateDFState} = useMutators(externalMutators); const {store: searchStore, setField, removeField, isHiddenField} = useSearchStore(); const context = React.useMemo( @@ -57,10 +57,10 @@ export const DynamicField: React.FC = ({ generateRandomValue, tools: {...tools, mutateDFState}, store, - mutators, + mutatorsStore, __mirror, }), - [tools, config, Monaco, __mirror, generateRandomValue, mutators, mutateDFState, store], + [tools, config, Monaco, __mirror, generateRandomValue, mutatorsStore, mutateDFState, store], ); const searchContext = React.useMemo( diff --git a/src/lib/core/components/Form/hooks/useMutators.ts b/src/lib/core/components/Form/hooks/useMutators.ts index 52a52b1b..2c7cacc6 100644 --- a/src/lib/core/components/Form/hooks/useMutators.ts +++ b/src/lib/core/components/Form/hooks/useMutators.ts @@ -2,15 +2,24 @@ import React from 'react'; import _ from 'lodash'; -import {DynamicFormMutators, SpecMutator} from '../types'; +import {FormValue} from '../../../types'; +import { + BaseValidateError, + DynamicFormMutators, + DynamicFormMutatorsStore, + SpecMutator, +} from '../types'; export const useMutators = (externalMutators?: DynamicFormMutators) => { - const firstRenderRef = React.useRef(true); - const [store, setStore] = React.useState(externalMutators || {}); - - const mutateDFState = React.useCallback( - (mutators: DynamicFormMutators) => { - const mergeSpec = (a: Record, b: Record) => { + const merge = React.useCallback( + ( + mutators: DynamicFormMutators, + store: DynamicFormMutatorsStore, + ): DynamicFormMutatorsStore => { + const mergeSpec = ( + a: DynamicFormMutatorsStore['spec'] = {}, + b: Record, + ) => { const result = _.cloneDeep(a); const getKeys = (parent: any): string[][] => { @@ -30,18 +39,47 @@ export const useMutators = (externalMutators?: DynamicFormMutators) => { }; getKeys(b).forEach((key) => { - _.set(result, key, _.get(b, key)); + _.set(result, [key[0], 'value', ...key.slice(1)], _.get(b, key)); + }); + + return result; + }; + + const mergeValuesOrErrors = ( + a: Record = {}, + b: Record, + ) => { + const result = _.cloneDeep(a); + + Object.keys(b).forEach((key) => { + _.set(result, [key, 'value'], b[key]); }); return result; }; - setStore((store) => ({ + return { ...store, - ...(mutators.errors ? {errors: _.merge(store.errors, mutators.errors)} : {}), - ...(mutators.values ? {values: _.merge(store.values, mutators.values)} : {}), - ...(mutators.spec ? {spec: mergeSpec(store.spec || {}, mutators.spec)} : {}), - })); + ...(mutators.errors + ? {errors: mergeValuesOrErrors(store.errors, mutators.errors)} + : {}), + ...(mutators.values + ? {values: mergeValuesOrErrors(store.values, mutators.values)} + : {}), + ...(mutators.spec ? {spec: mergeSpec(store.spec, mutators.spec)} : {}), + }; + }, + [], + ); + + const firstRenderRef = React.useRef(true); + const [store, setStore] = React.useState( + merge(externalMutators || {}, {}), + ); + + const mutateDFState = React.useCallback( + (mutators: DynamicFormMutators) => { + setStore((store) => merge(mutators, store)); }, [setStore], ); @@ -54,5 +92,5 @@ export const useMutators = (externalMutators?: DynamicFormMutators) => { } }, [externalMutators]); - return {mutators: store, mutateDFState}; + return {mutatorsStore: store, mutateDFState}; }; diff --git a/src/lib/core/components/Form/types/context.ts b/src/lib/core/components/Form/types/context.ts index ec53b58f..90e0b31c 100644 --- a/src/lib/core/components/Form/types/context.ts +++ b/src/lib/core/components/Form/types/context.ts @@ -8,6 +8,7 @@ import { DynamicFieldStore, DynamicFormConfig, DynamicFormMutators, + DynamicFormMutatorsStore, FieldValue, ValidateError, WonderMirror, @@ -25,6 +26,6 @@ export interface DynamicFormsContext { mutateDFState: (mutators: DynamicFormMutators) => void; }; store: DynamicFieldStore; - mutators: DynamicFormMutators; + mutatorsStore: DynamicFormMutatorsStore; __mirror?: WonderMirror; } diff --git a/src/lib/core/components/Form/types/mutators.ts b/src/lib/core/components/Form/types/mutators.ts index 8afe0544..15644b0a 100644 --- a/src/lib/core/components/Form/types/mutators.ts +++ b/src/lib/core/components/Form/types/mutators.ts @@ -22,3 +22,9 @@ export interface DynamicFormMutators { values?: Record; spec?: Record; } + +export interface DynamicFormMutatorsStore { + errors?: Record; + values?: Record; + spec?: Record; +} diff --git a/src/lib/kit/components/Inputs/TextArea/TextArea.tsx b/src/lib/kit/components/Inputs/TextArea/TextArea.tsx index 4749290a..594a28af 100644 --- a/src/lib/kit/components/Inputs/TextArea/TextArea.tsx +++ b/src/lib/kit/components/Inputs/TextArea/TextArea.tsx @@ -9,7 +9,7 @@ export const TextArea: StringInput = ({name, input, spec}) => { return (