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.