简体   繁体   中英

Using object as a value in `react-hook-form`

Description

I have a component that uses an object as a value.

I would like to use this component with react-hook-form

The problem is that react-hook-form thinks that my object is a nested form control

Setup

This is just an example.

Range date picker is a common use case for such a behaviour

Example codepen

The value that the component accepts:

type ComponentValue = {
  a: string;
  b: string;
}

The component:

const Component = ({ value, onChange }: { value: ComponentValue, onChange: (value: ComponentValue) => void }) => {
  return (
    <input value={value.a} onChange={e => onChange({ a: e.target.value, b: (Math.random() * 100 | 0).toString() }) } />
  )
}

Form value

type FormValues = {
  someField: ComponentValue;
  // other fields
};

Default values:

const defaultValues = {
  someField: {
    a: 'Default a',
    b: 'Default b'
  },
  // other fields
};

useForm usage:

const {
  register,
  handleSubmit,
  formState: { errors },
  control,
} = useForm<FormValues>({
  defaultValues,
});

The problem

Hovering over (or trying to use) errors reveals the type:

const errors: {
  someField?: {
    a?: FieldError | undefined;
    b?: FieldError | undefined;
  } | undefined;
  // other fields
}

But I would it to be:

const errors: {
  someField?: FieldError | undefined;
  // other fields
}

Summary

Can I somehow force react-hook-form to treat my object as a value instead of a nested form field?

If you want to combine 2 inputs in a same component, you can try the approach with a controller.

export default function App() {
  const { control, handleSubmit, watch } = useForm({
    defaultValues: {
      travel: {
        from: "paris",
        to: "london"
      }
    }
  });

  const onSubmit = (data) => {
    console.log("dans onSubmit", JSON.stringify(data, null, 2));
  };

  return (
    <>
      <h1>Travel</h1>

      <form
        onSubmit={handleSubmit(onSubmit)}
        style={{ display: "flex", flexDirection: "column", maxWidth: 600 }}
        noValidate
      >
        <TravelPicker control={control} />

        <input type="submit" />
        <pre>{JSON.stringify(watch(), null, 2)}</pre>
      </form>
    </>
  );

The component have a control props, to be able to register the 2 inputs:

const TravelPicker = ({ control }) => {
  return (
    <>
      <label>
        From
        <Controller
          name="travel.from"
          control={control}
          render={({ field: { onChange, value, name } }) => (
            <input name={name} value={value} onChange={(e) => onChange(e)} />
          )}
        />
      </label>
      <label>
        To
        <Controller
          name="travel.to"
          control={control}
          render={({ field: { onChange, value, name } }) => (
            <input name={name} value={value} onChange={(e) => onChange(e)} />
          )}
        />
      </label>
    </>
  );
};

The errors object has then the same structure as the form inputs.

See this sandbox for a functional exemple.

The createor of RHF seems to indicate here that you can also have only one component for 2 inputs.

You can use setValue and setError like below:

const {setValue, setError} = useForm();

// ... some where in your form
<input 
  type = "text"
  name = "from"
  onChange = {
    (event) => {
      const value = event.target.value;
      if(value === ""){
        setError("from", "This field is required");
      }else{
        setValue(`from[0]`, {
          "otherProperty" : "otherValue",
          "from_value": value, // this is your input value
        });
      }
    }
  }
/>

Probably you would be iterating through an array. change static 0 with your index in setValue If not you may use object syntax like below:

//....
setValue(`from.your_property_name`, {
    "otherProperty" : "otherValue",
    "from_value": value, // this is your input value
});

// ....

This won't cause re-render of component.

More info in doc

NOTE: Please be respectful when commenting.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM