Reactjs – Select Component’s inputProps (Material UI)

material-uireactjs

Following this question, I would like to replace the TextField (to input the age) component to use a Select component since I notice both have the inputProps property.

Original app:

function App() {
  const [state, setState] = React.useState({
    cats: [{ name: "cat1", age: "2" }, { name: "cat2", age: "5" }],
    owner: "Owner's Name"
  });
  const handleFormChange = e => {
    if (["name", "age"].includes(e.target.dataset.fieldType)) {
      const newCats = [...state.cats];
      newCats[e.target.dataset.id][e.target.dataset.fieldType] = e.target.value;
      setState({ ...state, cats: newCats });
    } else {
      setState({ ...state, [e.target.name]: e.target.value });
    }
  };
  return (
    <form onChange={handleFormChange}>
      <TextField label="Owner" value={state.owner} name="owner" />
      <br />
      <br />
      <TextField
        label="Name 1"
        value={state.cats[0].name}
        inputProps={{ "data-id": 0, "data-field-type": "name" }}
      />
      <TextField  <-----------------------replace with a Select
        label="Age 1"
        value={state.cats[0].age}
        inputProps={{ "data-id": 0, "data-field-type": "age" }}
      />
    </form>
  );
}

The Select component I'm replace the second TextField:

    <Select
      onChange={handleSelectChange}
      label={"Age 1"}
      value={state.cats[0].age}
      inputProps={{
        "data-id": idx,
        "data-field-type": "age",
        name: "customName"
      }}
    >
      <MenuItem value={10}>Ten</MenuItem>
      <MenuItem value={20}>Twenty</MenuItem>
      <MenuItem value={30}>Thirty</MenuItem>
    </Select>

But in my handleSelectChange function:

 const handleSelectChange = e => {
    console.log("value", e.target.value); //OK
    console.log("name", e.target.name); // OK
    console.log("dataset", e.target.dataset); //undefined
  };

The data attributes passed to the inputProps are undefined, why is that?

This is a codesandbox I made testing this behaviour: https://codesandbox.io/s/dynamic-form-change-handler-with-select-ft4dj

Best Answer

For Material-UI's Select, the event that triggers the onChange is actually a click on a MenuItem. Here is a slightly simplified (ignoring the multiple case) version of that code:

  const handleItemClick = child => event => {
    update(false, event);

    if (onChange) {
      const newValue = child.props.value;

      event.persist();
      event.target = { value: newValue, name };
      onChange(event, child);
    }
  };

Notice that the event target is explicitly created here as an object with only value and name properties. The original click event target would not generally be helpful since it wouldn't correspond to a consistent DOM element representing the Select, but would instead be some DOM element within the particular MenuItem that was clicked.

When answering your previous question, I avoided starting a discussion about whether the approach in the tutorial is a good one since I wanted to avoid getting into more opinion-based aspects; however, this scenario forces the discussion. In general, in React I think it is best to avoid putting extra stuff in the DOM so that event handlers can pull the information back out. Instead, just provide the event handler directly with the information.

For instance you could have:

  const handleSelectChange = (index, fieldType) => e => {
    console.log("value", e.target.value);
    console.log("name", e.target.name);
    console.log("index", index);
    console.log("fieldType", fieldType);
  };
// then later in the JSX:

            <Select
              onChange={handleSelectChange(idx, "age")}
              label={`Cat ${idx + 1} age`}
              value={state.cats[idx].age}
              inputProps={{
                name: "customName"
              }}
            >
              <MenuItem value={10}>Ten</MenuItem>
              <MenuItem value={20}>Twenty</MenuItem>
              <MenuItem value={30}>Thirty</MenuItem>
            </Select>

In the code snippet above, the index and field type are passed to handleSelectChange to return a change handler that knows that information without it ever needing to be part of the DOM.

Here's a modified version of your sandbox with this working: https://codesandbox.io/s/dynamic-form-change-handler-with-select-1sihp

This same approach could also be used for the text inputs.

Related Topic