fabian-hiller / valibot

The modular and type safe schema library for validating structural data 🤖
https://valibot.dev
MIT License
6.33k stars 204 forks source link

ValibotSchema to FormikValidationSchema #844

Closed incompletude closed 1 month ago

incompletude commented 2 months ago

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;
}>>
fabian-hiller commented 2 months ago

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

incompletude commented 2 months ago

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.

fabian-hiller commented 2 months ago

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

incompletude commented 2 months ago

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 commented 2 months ago

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.