fabian-hiller / valibot

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

Allow null default value while enforcing required validation #556

Closed g-kduvignau closed 5 months ago

g-kduvignau commented 6 months ago

Hi! 👋

I'm using valibot with react-hook-form, and I need one of my properties to be required but with a default value of null. When the form is submitted and the value is still null, it should not validate.

So far, I've managed this by creating a derived type from the output schema. However, I'm wondering if there's a more efficient way to handle this scenario?

I've added a simplified example of my scenario. The role selection isn't a typical input (here it's a clickable div, in my actual scenario it's a custom component).

Thank you!

import { valibotResolver } from "@hookform/resolvers/valibot";
import { Controller, useForm } from "react-hook-form";
import { nullable, object, type Output, string } from "valibot";

export const Form = () => {
  const { handleSubmit, control } = useForm<FormInput>({
    defaultValues: {
      name: "test",
      role: null,
      secondaryRole: null
    },
    resolver: valibotResolver(FormSchema)
  });

  const onSubmit = (data: FormInput) => {
    console.log(data.role!.name);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <Controller 
          control={control}
          name="role"
          render={({ field: { onChange } }) => (
            <div onClick={() => onChange({ id: "role1", name: "Role 1" })}>Select role</div>
          )}
        />
        <button type="submit">Submit</button>
      </div>
    </form>
  );
};

export const FormSchema = object({
  name: string(),
  role: object({
    id: string(),
    name: string()
  }),
  secondaryRole: nullable(
    object({
      id: string(),
      name: string()
    })
  )
});

type FormOutput = Output<typeof FormSchema>;

type FormInput = Omit<FormOutput, "role"> & {
  role: FormOutput["role"] | null;
};
fabian-hiller commented 6 months ago

Hi 👋🏼 this will be possible soon once #502 is merged. Until then, you can use the following workaround:

import * as v from 'valibot';

const FormSchema = v.object({
  name: v.string(),
  role: v.nullable(
    v.object({
      id: v.string(),
      name: v.string(),
    }),
    { id: '', name: '' }
  ),
  secondaryRole: v.nullable(
    v.object({
      id: v.string(),
      name: v.string(),
    })
  ),
});

type FormInput = v.Input<typeof FormSchema>;
type FormOutput = v.Output<typeof FormSchema>;
fabian-hiller commented 5 months ago

502 is merged 🚀