diff --git a/src/hooks/useFormState/useFormState.stories.mdx b/src/hooks/useFormState/useFormState.stories.mdx index c5f5270..c30742a 100644 --- a/src/hooks/useFormState/useFormState.stories.mdx +++ b/src/hooks/useFormState/useFormState.stories.mdx @@ -36,7 +36,10 @@ PatternFly text fields driven by this hook. function useFormField( initialValue: T, schema: [T] extends [Array] ? yup.ArraySchema : yup.Schema, - options?: { initialTouched?: boolean } + options: { + initialTouched?: boolean; // Start field with isTouched set to true + onChange?: (newValue: T) => void; // Called after any call to field.setValue, for side effects + } = {} ): IFormField; function useFormState( diff --git a/src/hooks/useFormState/useFormState.ts b/src/hooks/useFormState/useFormState.ts index 24a83b3..f8069ba 100644 --- a/src/hooks/useFormState/useFormState.ts +++ b/src/hooks/useFormState/useFormState.ts @@ -64,12 +64,30 @@ export interface IFormState { export const useFormField = ( initialValue: T, schema: yup.AnySchema, - options: { initialTouched?: boolean } = {} + options: { + initialTouched?: boolean; // Start field with isTouched set to true + onChange?: (newValue: T) => void; // Called after any call to field.setValue, for side effects + } = {} ): IFormField => { const [defaultValue, setDefaultValue] = React.useState(initialValue); // The value used on clear() const [cleanValue, setCleanValue] = React.useState(initialValue); // The value considered "unchanged", determines isDirty and used on revert() - const [value, setValue] = React.useState(initialValue); // The actual value in the field + const [value, baseSetValue] = React.useState(initialValue); // The actual value in the field const [isTouched, setIsTouched] = React.useState(options.initialTouched || false); + + // Replicates the behavior of a real useState dispatch function in order to also call `options.onChange` if present + const setValue: React.Dispatch> = ( + valueOrFn: T | ((prevValue: T) => T) + ) => { + baseSetValue(valueOrFn); + if (options.onChange) { + const newValue = + typeof valueOrFn === 'function' + ? (valueOrFn as (prevValue: T) => T)(value) + : (valueOrFn as T); + options.onChange(newValue); + } + }; + return { value, setValue,