Search code examples
reactjsreact-reduxreact-hook-formreact-typescript

React hook form not populating with fetched data


I have a form, which is part of a tabbed set of panels. The form itself looks like this:

import * as React from "react";
import { useForm, SubmitHandler, Controller } from "react-hook-form";
import { Char } from "../../models";
import { strikerTheme } from "../../../shared/components/StrikerTheme";
import ThemeProvider from "@mui/material/styles/ThemeProvider";
import TextField from "@mui/material/TextField";
import MenuItem from "@mui/material/MenuItem";
import InputAdornment from "@mui/material/InputAdornment";
import { useAppSelector } from "../../../../app/hooks";
import { getTraits, getIdeals } from "../../../shared/specSlice";
import { Quality } from "../../models/specs";
import { CheckboxGroup } from ".";


export const DescriptionForm = ({
  onSubmit,
  data
}: {
  data: Partial<Char>;
  onSubmit: SubmitHandler<Partial<Char>>;
}) => {

  const specState = useAppSelector(state => state.specState);

  console.log('imitial data')
  console.log(data)
  const { handleSubmit, control } = useForm({defaultValues: data});

  const alignments = ['Lawful Good', 'Neutral Good', 'Chaotic Good', 'Lawful Neutral', 'Neutral', 'Chaotic Neutral', 'Lawful Evil', 'Neutral Evil', 'Chaotic Evil']
  const traits: Quality[] = getTraits(specState.specKeys);
  const ideals: Quality[] = getIdeals(specState.specKeys);


  return data && (

    <ThemeProvider theme={strikerTheme}>
      <form className='form' onSubmit={handleSubmit(onSubmit)}>
        <h4>Description</h4>
        <div className='form-row'>
          <Controller name="name" control={control} render={({ field }) => <TextField sx={{ width: '40ch' }} {...field} label="character name" />} />
        </div>
        <div className='form-row'>
          <Controller name="experience" control={control} render={({ field }) => <TextField {...field} label="experience" />} />
          <Controller name="levels" control={control} render={({ field }) => <TextField {...field} label="level" />} />
        </div>
        <div className='form-row'>
          <Controller name="age" control={control} render={({ field }) => <TextField {...field} label="age" />} />
          <Controller name="gender" control={control} render={({ field }) => <TextField {...field} label="gender" />} />
          <Controller name="height" control={control} render={({ field }) => <TextField {...field} label="height" />} />
          <Controller name="weight" control={control} render={({ field }) =>
            <TextField {...field} label="weight" InputProps={{
              endAdornment: <InputAdornment position="end">kg</InputAdornment>,
            }}
            />}
          />
        </div>
        <div className='form-row'>
          <Controller
            name="alignment"
            control={control}
            render={({ field }) => <TextField {...field} label="alignment" helperText="Please select your alignment" select>
              {alignments.map((option) => (
                <MenuItem key={option} value={option}>
                  {option}
                </MenuItem>
              ))}
            </TextField>}
          />
          <div className='form-row'>
            <Controller name="traits" control={control} render={({ field }) => <CheckboxGroup
              control={control}
              label="traits"
              options={traits}
              {...field}
            />
            } />
          </div>

          <div className='form-row'>

            <Controller name="ideals" control={control} render={({ field }) => <CheckboxGroup
              control={control}
              label="ideals"
              options={ideals}
              {...field}
            />
            } />
          </div>

        </div>
        <button
          type='submit'
          className='btn btn-block btn-danger'
        >
          Submit
        </button>

      </form>
    </ThemeProvider>

  )
};

note that the console.log statements show that the data are fully populated, for example:

enter image description here

and yet the form renders as if this data is blank (i.e., not undefined but with its initialised default values).

The onSubmit is actually in the parent component, and is rather simple:

const onSubmit = async (
    data: Partial<Char>
  ) => {
    if (charstate.char) {
      await dispatch(editChar(data));
    } else {
      // its a new char, so create 

      await dispatch(createChar(data));
    }
  };

and it works, in other words, I can fill the form out, and the form is submitted and saved to the datastore.

I have tried a variety of ways to either have the form wait until the values are populated, or try to force the controls to read the values (depending on whether this is an async issue or referencing issue). It feels like i'm missing something simple.

what am i missing?


Solution

  • Please try

    const { handleSubmit, control } = useForm(values: data);
    

    as described in the docs

    The values props will react to changes and update the form values, which is useful when your form needs to be updated by external state or server data.