reactjsreact-reduxmaterial-uireact-forms

How to set objects as form values in react with Material UI?


I'm currently trying to build a form with React and Material UI, where my final payload has to look like this:

{
  title: title,
  quadrants: {
    0: { name: quadrantOne },
    1: { name: quadrantTwo },
    2: { name: quadrantThree },
    3: { name: quadrantFour }
  }
}

The corresponding part of my form (including initial values) is structured as follows:

const initialValues = {
  title: '',
  quadrants: {},
};

const [values, setValues] = useState(initialValues);

const handleInputChange = (e: any) => {
  const { name, value } = e.target;
  setValues({
    ...values,
    [name]: value
  });
};

return (
    <Form>
        <Grid item xs={12} sm={12}>
          <Input
            name="title"
            label="Title"
            value={initialValues.title}
            onChange={handleInputChange}
          />
        </Grid>
        <Grid item xs={12} sm={6}>
          <Input
            name="quadrant-one"
            label="Quadrant 1"
            value={initialValues.quadrants}
            onChange={handleInputChange}
          />
        </Grid>
        <Grid item xs={12} sm={6}>
          <Input
            name="quadrant-two"
            label="Quadrant 2"
            value={initialValues.quadrants}
            onChange={handleInputChange}
          />
        </Grid>
    </Form>
)

In this way, my input fields look like this, so that I cannot enter anything in the fields::

enter image description here

But if I enter something in the fields (the entry is not visible), then I get the following payload:

{
  title: "Test"
  quadrants: {}
  quadrant-one: "[object Object]"
  quadrant-two: "[object Object]"
}

The entries are not nested in the "quadrants" object, but entries appear in the payload with the name of the respective form element (Like quadrant-one, quadrant-two..).

What am I doing wrong here and how can I fix this?


Solution

  • You are getting [Object Object] because initialValues.quadrants is an object, I assume you are trying to set initialValues.quadrants[0].name, but as initial value initialValues.quadrants[0] is undefined

    You need to apply some changes. I recommend you define a custom attribute (data-index) for the quadrants input, this way on input change you can update the correct object. Also for the quadrants inputs you can define a function to get the value because at first render it value is undefined:

    const initialValues = {
        title: "",
        quadrants: {}
      };
    
      const [values, setValues] = useState(initialValues);
    
      const handleInputChange = e => {
        const { name, value } = e.target;
        const dataIndex = Number(e.target.getAttribute("data-index"));
        setValues(prev => {
          if (name === "title") {
            return {
              ...prev,
              [name]: value
            };
          } else {
            return {
              ...prev,
              quadrants: {
                ...prev.quadrants,
                [dataIndex]: { name: value }
              }
            };
          }
        });
      };
    
      const getValue = prop => {
        const value = values.quadrants[prop] ? values.quadrants[prop].name : "";
        return value;
      };
    
    return (
        <Form>
            <Grid item xs={12} sm={12}>
              <Input
                name="title"
                label="Title"
                value={values.title}
                onChange={handleInputChange}
              />
            </Grid>
            <Grid item xs={12} sm={6}>
              <Input
                name="quadrant-one"
                label="Quadrant 1"
                data-index="0"
                value={getValue(0)}
                onChange={handleInputChange}
              />
            </Grid>
            <Grid item xs={12} sm={6}>
              <Input
                name="quadrant-two"
                label="Quadrant 2"
                data-index="1"
                value={getValue(1)}
                onChange={handleInputChange}
              />
            </Grid>
        </Form>
    )