Home > Mobile >  MUI Object in Select default value not showing but shows when object selected
MUI Object in Select default value not showing but shows when object selected

Time:04-24

As the code shows options has a "chosenValue" by default, however I have to change it for something to show by default in the dropdown or the dropdown is empty. How can I make it so when the page loads it by default value show "chosenValue"? Here is a codesandbok example: https://codesandbox.io/s/practical-noether-dwt52t?file=/src/App.js:0-1421

import "./styles.css";
import {
  FormControl,
  InputLabel,
  Select,
  MenuItem,
  OutlinedInput
} from "@mui/material";
import { useState } from "react";
export default function App() {
  const [options, setOptions] = useState({
    chosenValue: {
      label: "apple",
      color: "green"
    },
    aviableValues: [
      { label: "apple", color: "red" },
      { label: "Orange", color: "Orange" },
      { label: "pear", color: "green" }
    ]
  });
  const handleContentChange = (event) => {
    setOptions((prevState) => ({
      ...prevState,
      chosenValue: event.target.value
    }));
  };
  return (
    <>
      {options.chosenValue && (
        <FormControl sx={{ mt: 2 }}>
          <InputLabel id="demo-simple-select-helper-label">Fruits</InputLabel>
          <Select
            labelId="demo-simple-select-label"
            id="demo-simple-select"
            value={options.chosenValue}
            onChange={(e) => handleContentChange(e)}
            input={<OutlinedInput label={options.chosenValue.label} />}
            fullWidth
          >
            {options.aviableValues.map((item) => {
              return (
                <MenuItem value={item} key={item}>
                  {item.label}
                </MenuItem>
              );
            })}
          </Select>
        </FormControl>
      )}
      {options.chosenValue && <p>{options.chosenValue.label}</p>}
    </>
  );
}

CodePudding user response:

Working example in Code Sandbox

I have forked your example in Code Sandbox and modified it with a working implementation. I have also included an Autocomplete alternative.

https://codesandbox.io/s/musing-dew-58frco

Why selected option isn't displaying

As Ivan has stated, the reason why your Select component isn't showing the selected value is because the selected value is a different object than the value in the options and the Select component requires that the objects are strictly equal (===).

For example, although these objects appear to be the same, they will fail the strict equality check. You can test this example in your console, which will result in false.

{ fruit: "apple" } === { fruit: "apple" } 

How to use Select

The easiest way to use Select is for your value to be a string and your options to each have a label and value key (both strings). The label is what will be displayed on the screen and the value is what will be selected when the option is chosen.

For example, for your options, you could have:

const options = [
  { label: "Apple", color: "red", value: "apple" },
  { label: "Orange", color: "orange", value: "orange" },
  { label: "Pear", color: "green", value: "pear" }
];

Instead of storing your options in state, you could have it as a variable outside of state since it is not changing.

You only need to keep state for the selected value, which I'll call fruit. apple is the initial value of fruit.

const [fruit, setFruit] = useState("apple");

In your Select component, assign fruit to the value prop and ensure that the value of each item is provided to the value prop of MenuItem:

<Select
  labelId="demo-simple-select-label"
  id="demo-simple-select"
  value={fruit} // this is coming from state
  onChange={(e) => setFruit(e.target.value)} // we can setFruit directly here
  input={<OutlinedInput label="Hello" />}
  fullWidth
>
  {options.map((item) => {
    return (
      <MenuItem value={item.value} key={item.value}>
        {item.label}
      </MenuItem>
    );
  })}
</Select>

Instead of having a separate handleChange function, we can setFruit directly within the onChange prop.

The defaultValue is not required and should only be used if the component is not controlled. This is from the Material UI docs:

The default value. Use when the component is not controlled.

Ours is a controlled component since we are providing value from state (fruit) and also handling the change (setFruit).

Alternative is to use Autocomplete - if you wanted to selected item as an object

In the explanation above, I am using string values i.e. apple, orange and pear. If you wanted your selected values to be objects instead, e.g. { label: "Apple", color: "red", value: "apple" }, then you can use Material UI's Autocomplete.

We have the same options:

const options = [
  { label: "Apple", color: "red", value: "apple" },
  { label: "Orange", color: "orange", value: "orange" },
  { label: "Pear", color: "green", value: "pear" }
];

But our state is different:

const [fruit, setFruit] = useState({
  label: "Apple",
  color: "red",
  value: "apple"
});

Note that the initial state here must match exactly to one of the options.

The Autocomplete component provides a isOptionEqualToValue prop which compares the value we have selected to the options we can select from to determine which it should render. We want to compare the value key of the selected item and the option items. If they are equal, the option that matches will be displayed. For example, if the selected item is { label: "Apple", value: "apple" } and there is an option that is also { label: "Apple", value: "apple" }`, since both their values are equal ("apple" === "apple" is true), then "Apple" will be displayed as selected.

Now putting it altogether with the Autocomplete component:

<Autocomplete
  options={options}
  value={fruit}
  isOptionEqualToValue={(option, value) => option.value === value.value}
  onChange={(event, newValue) => {
    setFruit(newValue);
  }}
  renderInput={(params) => (
    <TextField {...params} label="Fruits" variant="standard" />
  )}
/>;

Hope this helps.

CodePudding user response:

The issue you are encountering is that object comparisons only evaluate to true if you compare the same exact object. (See https://stackoverflow.com/a/11705017/18920728)

options.aviableValues[0] === options.chosenValue  // false

To fix this, you can tell Mui how an option object should be rendered explicitly.

<Select
  // other props...
  renderValue={(o) => o.label}  // add this line
>

Alternatively, you can split your options state into 2 states:

const [options] = useState([
  { label: "apple", color: "red" },
  { label: "Orange", color: "Orange" },
  { label: "pear", color: "green" }
]);
const [selected, setSelected] = useState(options[0]);

...

<Select
  ...
  value={selected}
  ...
>

Note that since you are controlling the select component, there is no need to specify the default prop in <Select>, the default value is already specified as the default state when useState hook is called.

  • Related