4

I have input radio in React Hook Form and I try to use it as real boolean, instead of string.

I know that RHF have valueAsNumber to convert it as number. I thought that setValueAs was a generic way to allow any conversion but I can't make it work.

Code Sanbox


const schema = z.object({
  name: z.string().min(4, { message: "4 caractères minimum pour le name" }),
  developer: z.boolean() // A real boolean is expected here
});

export default function App() {
  const {
    register,
    handleSubmit,
    watch,
    formState: { errors }
  } = useForm({
    resolver: zodResolver(schema),
    defaultValues: {
      name: "John",
      developer: false // default as boolean
    }
  });

  return (
    <form onSubmit={handleSubmit(onSubmit)} noValidate>
      <input type="text" placeholder="Name" {...register("name")} />

      <p>
        Is developer ?&nbsp;
        <label>
          Oui
          <input
            {...register("developer", {
              setValueAs: (val) => true // Try to make it as a real boolean but doesn't work
            })}
            type="radio"
            value={true}
          />
        </label>
        <label>
          Non
          <input
            {...register("developer", {
              setValueAs: (val) => false
            })}
            type="radio"
            value={false}
          />
        </label>
      </p>

      <input type="submit" />

    </form>
  );
}

Any idea?

2
  • 'use it as real boolean, instead of string' you need to specify your question. Commented Jun 17, 2022 at 13:15
  • 1
    How to be clearer? I don't want that the value of input to be saved as a string, as native inputs do. I want to preserve the nature of the data wich is a real boolean. Not "true" and "false". This is a question of web api and React Hook Form, not a JS one. Commented Jun 17, 2022 at 15:08

8 Answers 8

11

I find out how to get a boolean from a radio input in RHF.

The solution with setValueAs, as I tried before, only works for text input (like type="text" or type="number"). For radio button, even if the value is a string, it doesn't work.

So it can be solved by using a Controller component.

Code sandbox

function MyBooleanInput({ control, name }) {
  return (
    <Controller
      control={control}
      name={name}
      render={({ field: { onChange, onBlur, value, ref } }) => (
        <>
          <label>
            Oui
            <input
              type="radio"
              onBlur={onBlur} // notify when input is touched
              onChange={() => onChange(true)} // send value to hook form
              checked={value === true}
              inputRef={ref}
            />
          </label>
          <label>
            Non
            <input
              type="radio"
              onBlur={onBlur} // notify when input is touched
              onChange={() => onChange(false)} // send value to hook form
              checked={value === false}
              inputRef={ref}
            />
          </label>
        </>
      )}
    />
  );
}

This way, values are real booleans and zod doesn't throw an error.

Sign up to request clarification or add additional context in comments.

Comments

1

Potential solution using setValue on the button to convert its type to boolean with React Hook Form :

    export function forceTypeBoolean(value: string | null | boolean): boolean | null {
       return typeof value === 'boolean' ? value : value === 'true' ? true : value === 'false' ? false : null;
    }
    // use
    <button
      {...register("found", { setValueAs: forceTypeBoolean, })}
      onClick={() => setValue("found", true)}/>

Comments

1

use

developer: z.coerce.boolean()

instead and then just use "false" and "true" as the values for the radio buttons

Comments

0

@pom421 in my case setValueAs function as worked as expected. The only difference I use MUI as UI framework, but it's unlikely it's the cause.

I just made a helper:

helper.ts

import { RegisterOptions } from 'react-hook-form';

export function forceHtmlRadioOutputToBeBoolean(value: string | null): boolean | null {
  if (value === 'true') {
    return true;
  } else if (value === 'false') {
    return false;
  } else if (value === null) {
    // Sometimes the radio group can be optional, allow this case
    return null;
  } else {
    throw new Error(`this radio group is supposed to only manage string boolean values ("true", "false"), or can optionally be null.`);
  }
}

export const reactHookFormBooleanRadioGroupRegisterOptions = {
  setValueAs: forceHtmlRadioOutputToBeBoolean,
} as RegisterOptions;

And in a random form I can:

                <FormControlLabel
                  value="true"
                  control={<Radio />}
                  {...register('alreadyRequestedInThePast', reactHookFormBooleanRadioGroupRegisterOptions)}
                  label="Yes, I do"
                />

Note that in my case MUI always converts value={true} into a string "true"... that's why I force the developer to manage strings on the templating side, but then, he will have a boolean in React Hook Form (and zod or other validating library).

Comments

0

I found the solution.

const { register, handleSubmit, setValue } = useForm<Form>({ defaultValue: ... });

<form onSubmit={handleSubmit(onSubmit)}>
  <input
    type="radio"
    onChange={(e) => setValue('fieldName', getRadioBooleanValue(e))
  />
</form>

1 Comment

What's getRadioBooleanValue?
0

If you do not want to change your existing structure and still want boolean values then you can use :

 setValueAs: (value: string) => (value === 'true' ? true || value === 'false' : false || value),

in the register as a very tiny quick fix.

Comments

0

If you have an object set as boolean value with zod you can manage it like this. It took me 1 hour, it shouldn't take you

<NativeSelect
        {...register('active', { setValueAs: Boolean })}
        label="Yayınlama Durumu"
        withAsterisk
        onChange={(e) => setValue('active', e.currentTarget.value === 'true')} 
        data={[
          { value: 'true', label: 'Yayında' },
          { value: 'false', label: 'Yayında Değil' },
        ]}
        classNames={{ label: 'font-semibold text-md mb-1' }}
        defaultValue={product.active ? 'true' : 'false'} // Default değer
        variant="filled"
        error={errors.active?.message}
      />

this my schema for active active: z.boolean().default(false),

Comments

-1

boolean_value = (true_false_String === "true")

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.