Skip to content

Commit

Permalink
feat(useFormField): Add onChange option for side-effects whenever any…
Browse files Browse the repository at this point in the history
…thing calls field.setValue (#88)
  • Loading branch information
mturley authored Feb 11, 2022
1 parent dd4759b commit d66f1e5
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 3 deletions.
5 changes: 4 additions & 1 deletion src/hooks/useFormState/useFormState.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ PatternFly text fields driven by this hook.
function useFormField<T>(
initialValue: T,
schema: [T] extends [Array<infer E>] ? yup.ArraySchema<E> : yup.Schema<T>,
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<T>;

function useFormState<TFieldValues>(
Expand Down
22 changes: 20 additions & 2 deletions src/hooks/useFormState/useFormState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,30 @@ export interface IFormState<TFieldValues> {
export const useFormField = <T>(
initialValue: T,
schema: yup.AnySchema<T | undefined>,
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<T> => {
const [defaultValue, setDefaultValue] = React.useState<T>(initialValue); // The value used on clear()
const [cleanValue, setCleanValue] = React.useState<T>(initialValue); // The value considered "unchanged", determines isDirty and used on revert()
const [value, setValue] = React.useState<T>(initialValue); // The actual value in the field
const [value, baseSetValue] = React.useState<T>(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<React.SetStateAction<T>> = (
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,
Expand Down

0 comments on commit d66f1e5

Please sign in to comment.