Skip to content

Instantly share code, notes, and snippets.

@devgioele
Last active May 5, 2023 07:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save devgioele/46784cd2f6a4068b3799e488284f8a98 to your computer and use it in GitHub Desktop.
Save devgioele/46784cd2f6a4068b3799e488284f8a98 to your computer and use it in GitHub Desktop.
An extension of react-hook-form's `useFormContext`
import _ from 'lodash'
import { useRef } from 'react'
import {
FieldError,
FieldPath,
FieldValues,
useFormContext,
} from 'react-hook-form'
import { DistributivePick } from '../typescript/DistributivePick'
export type FieldState = {
invalid: boolean
isDirty: boolean
isTouched: boolean
error?: FieldError
}
export const useAugmentedFormContext = <TFieldValues extends FieldValues>() => {
const methods = useFormContext<TFieldValues>()
const dirtyRegistryRef = useRef<Record<string, boolean | undefined>>({})
const getValues = <TFieldName extends FieldPath<TFieldValues>>(
names: TFieldName[]
) => {
const values = methods.getValues(names)
const obj = _.zipObject(names, values)
return obj as DistributivePick<TFieldValues, TFieldName>
}
const getFieldStates = <TFieldName extends FieldPath<TFieldValues>>(
names: TFieldName[]
) => {
const fieldStates = {} as Record<TFieldName, FieldState>
for (const name of names) {
fieldStates[name] = methods.getFieldState(name)
}
return fieldStates
}
const isOrWasDirty = <TFieldName extends FieldPath<TFieldValues>>(
name: TFieldName
) => {
const { isDirty } = methods.getFieldState(name)
if (isDirty) {
dirtyRegistryRef.current[name] = true
}
return dirtyRegistryRef.current[name] ?? false
}
const isOrWasAtLeastOneDirty = <TFieldName extends FieldPath<TFieldValues>>(
names: TFieldName[]
) => {
const dirtyValues = names.map((name) => isOrWasDirty(name))
return dirtyValues.some((dirty) => dirty)
}
return {
...methods,
getValues,
getFieldStates,
isOrWasDirty,
isOrWasAtLeastOneDirty,
}
}
@devgioele
Copy link
Author

One may also avoid importing lodash by using:

const zipObject = <T>(keys: PropertyKey[], values: T[]): Record<string, T> =>
  Object.fromEntries(keys.map((key, index) => [key, values[index]]))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment