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

1.9k Views Asked by At

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?

2

There are 2 best solutions below

2
On BEST ANSWER

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>
)
3
On

Try this fixes:-

  • change input value from value={initialValues.title} to value={values.title}. Since you already set your initialValues to state values. The onChange event only be reflected on state values being updated with setValues. Not directly on const initialValues (do the same for the rest of the inputs - only inputs that use values state)

  • function to handleTitleChange()

const handleTitleChange = (e: any) => {
  const { name, value } = e.target;
  
  setValues({
    ...values,
    [name]: value
  });
};
  • function to handleQuadrantChange()
const handleQuadrantChange = (e: any) => {
  const { name, value } = e.target;
  
  setValues({
    ...values,
    quadrants: {
      ...values.quadrants,
      [name]: value
    }
  });
};