valibot icon indicating copy to clipboard operation
valibot copied to clipboard

ValibotSchema to FormikValidationSchema

Open incompletude opened this issue 1 year ago • 5 comments

Not sure if Valibot provides integrations, or if a docs page could provide the snippet, but I don't want to write another lib just for this. In case anyone needs, here is how I'm using Valibot Schemas in Formik.

export const withSchema =
  <T>(schema: v.BaseSchema<unknown, T, v.BaseIssue<unknown>> | v.BaseSchemaAsync<unknown, T, v.BaseIssue<unknown>>) =>
  async (values: T): Promise<Partial<T>> => {
    const result = await v.safeParseAsync(schema, values)

    if (result.success) {
      return {}
    }

    return result.issues.reduce((acc, curr) => {
      if (curr.path && curr.path.length > 0) {
        const key = curr.path[0].key as string

        return {
          ...acc,
          [key]: curr.message,
        }
      }

      return acc
    }, {})
  }

const formikSchema = withSchema(ExampleValibotSchema)

// result will be

const formikSchema: (values: {
  exampleField: string;
}) => Promise<Partial<{
  exampleField: string;
}>>

incompletude avatar Sep 20 '24 21:09 incompletude

Is Formik still maintained? Do you see any chance to add Valibot support to Formik?

fabian-hiller avatar Sep 21 '24 02:09 fabian-hiller

Is Formik still maintained? Do you see any chance to add Valibot support to Formik?

Seems like they are pushing a few things here and there, but they have so many PRs and issues, huge community tho. There are some issues related to plugins for validation libs, but they are stalled for years.

https://github.com/jaredpalmer/formik/issues/3458

So, I'm not sure if this would be accepted. Now that you asked, maybe https://github.com/react-hook-form/react-hook-form is a better alternative.

incompletude avatar Sep 21 '24 14:09 incompletude

I write the Valibot adapter for React Hook Form. If you can switch, you probably should.

fabian-hiller avatar Sep 22 '24 01:09 fabian-hiller

I write the Valibot adapter for React Hook Form. If you can switch, you probably should.

I tried yesterday, but I find react-hook-form a bit ugly and intrusive. I will try again. Thanks.

incompletude avatar Sep 22 '24 21:09 incompletude

Ok, i wrote a wrapper around react-hook-form to turn its API more bearable for my usecase:

import { valibotResolver } from "@hookform/resolvers/valibot"
import { ChangeEvent } from "react"
import { DefaultValues, FieldValues, Path, PathValue, SubmitHandler, useForm, useWatch } from "react-hook-form"
import { AnySchema } from "valibot"

type Props<T extends FieldValues> = {
  initialValues: DefaultValues<T>
  schema: AnySchema
  onSubmit: SubmitHandler<T>
  onChange?: (name: Path<T>, value: PathValue<T, Path<T>>) => void
}

function useFormState<T extends FieldValues>({ initialValues, schema, onSubmit, onChange }: Props<T>) {
  const form = useForm({
    defaultValues: initialValues,
    resolver: valibotResolver(schema),
  })

  const touches = form.formState.touchedFields

  const errors = form.formState.errors

  const watchers = Object.keys(initialValues).reduce(
    (acc, key) => {
      acc[key as keyof T] = useWatch({ control: form.control, name: key as Path<T> })
      return acc
    },
    {} as Record<keyof T, T[keyof T]>,
  )

  const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
    const { name, value } = event.target

    const path = name as Path<T>
    const pathValue = value as PathValue<T, Path<T>>

    form.setValue(path, pathValue)

    if (onChange) {
      onChange(path, pathValue)
    }
  }

  const handleSubmit = form.handleSubmit(onSubmit)

  const setValue = form.setValue

  return {
    touches,
    errors,
    watchers,
    handleChange,
    handleSubmit,
    setValue,
  }
}

export { useFormState }

Someone already wrote a resolver for valibot and react-hook-form. thanks fabian.

incompletude avatar Sep 22 '24 23:09 incompletude