reactjsreact-hook-formreact-select

React-hook-form fields cannot read from object in state


Codesandbox: https://codesandbox.io/p/sandbox/codesandboxer-example-forked-mypffg?file=%2Fexample.tsx%3A105%2C44

I have a react-select async component making requests to an API. I would like to display the various fields of the resulting object in react-hook-form fields. When I register these fields with RHF, they can successfully write to form state but don't seem to react to it. Whether I set form state with the async component or explicit setValue calls, the input don't show the value.

I have tried using both registering the inputs and Controllers wrapping an input in the example and neither one responds to updates to form state

const formMethods = useForm<FormFields>({
    defaultValues: {
      todo: {
        title: "",
        id: "",
      },
    },
  });

  return (
      <div className="row">
        <Controller
          control={formMethods.control}
          name={"todo"}
          render={(params) => (
            <AsyncCreatableSelect
              defaultOptions
              getOptionLabel={(todo) => todo.title}
              getOptionValue={(todo) => todo.id}
              loadOptions={promiseOptions}
              {...params.field}
            />
          )}
        />
        <div>
          <Controller
            control={formMethods.control}
            name={"todo.id"}
            render={(params) => {
              return (
                <label>
                  Controller-bound
                  <input {...params.field} value={params.field.value} />
                </label>
              );
            }}
          />
        </div>
        <label>
          Register-bound
          <input {...formMethods.register("todo.title")} />
        </label>
        <div>
          <button
            onClick={() => {
              formMethods.setValue(
                "todo",
                {
                  userId: 1,
                  id: 1,
                  title: "delectus aut autem",
                  completed: false,
                },
                { shouldDirty: true, shouldTouch: true }
              );
            }}
          >
            Set Object Value
          </button>
        </div>
      </div>

Solution

  • Solution Sandbox: https://codesandbox.io/p/sandbox/codesandboxer-example-forked-4q34n8?file=%2Fexample.tsx%3A42%2C15

    The trick is to set the (sub)fields explicitly:

    onChange={(e) => {
      formMethods.setValue("todo.completed", e.completed);
      formMethods.setValue("todo.id", e.id);
      formMethods.setValue("todo.title", e.title);
      formMethods.setValue("todo.userId", e.userId);
    }}
    

    This triggers the fields to update