Material UI Select组件值为列表项时无法显示选中选项
问题描述
我有一个MUI React Select组件,原本将更新后的event.value作为简单字符串使用时,组件可以正常工作,原代码如下:
import React, { useState, useEffect, Fragment } from 'react'; import { Box, Input, MenuItem, FormControl, Select, InputLabel, CircularProgress, Button } from '@mui/material'; import { createTheme, ThemeProvider } from '@mui/material/styles'; import axios from "axios"; function ProductSelect() { const [loadingData, setLoadingData] = useState(true); const [productType, setProductType] = useState(''); const [productTypes, setProductTypes] = useState([]); const [runmode, setRunmode] = useState('dev'); async function getProducts() { await axios .get(`https://myendpoint.myregion.mycloud.com/${ runmode }/product_types`, { headers: { 'Content-Type': 'application/json' } }) .then((response) =>{ var test_data = response.data.data; setProductTypes( test_data.map((x) => { return { product_name: `${x.product_name}`, product_type_id: parseInt(`${x.product_type_id}`), } }) ); setLoadingData(false); }); } useEffect(() => { getProducts(); if (loadingData) { getProducts(); } }, []); const handleProductTypeChange = (event) => { setProductType(event.target.value); }; const theme = createTheme({ palette: { neutral: { main: '#24507C', contrastText: '#fff', }, }, }); return ( <div> <ThemeProvider theme={theme}> {loadingData ? ( <Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', marginBottom: '185px' }}> <CircularProgress/> <span style={{ justifyContent: "center", position: "fixed", top: "30%" }}>Loading. Please wait...</span> </Box> ) : ( <> <FormControl style={{width: '250px', marginTop: '0px', marginBottom: '30px'}}> <InputLabel id="demo-simple-select-label">Product Type</InputLabel> <Select labelId="demo-simple-select-label" id="demo-simple-select" value={productType} label="Product Type" onChange={handleProductTypeChange} > {productTypes.map((option) => ( <MenuItem key={option.product_type_id} value={option.product_name} > {option.product_name} </MenuItem> ))} </Select> </FormControl> </> )} <Button color="neutral" variant="contained" cursor="pointer">Schedule</Button> </ThemeProvider> </div> ); }; export default ProductSelect;
但productTypes中的每个项包含product_type_id和product_name两个键值对,我希望将MenuItem的value改为数组,同时传递这两个值,修改后的MenuItem代码如下:
<MenuItem key={option.product_type_id} value={[option.product_name, option.product_type_id]} >
并在处理函数中取数组第一个元素更新productType:
const handleProductTypeChange = (event) => { setProductType(event.target.value[0]); };
此时处理函数能获取到正确值,但选中的选项无法显示,请问我哪里出错了?
问题原因及解决方案
核心问题
MUI的Select组件通过**严格相等(===)**匹配Select的value属性和MenuItem的value,以此确定选中项并显示。你当前的Select绑定的productType是字符串类型,但MenuItem的value是数组类型,两者类型不匹配,严格相等比较失败,导致组件无法识别选中项,所以不会显示选中内容。
解决方案
统一Select和MenuItem的value类型即可,推荐用对象传递多值(比数组可读性更强):
- 修改
MenuItem的value为对象:
<MenuItem key={option.product_type_id} value={{ name: option.product_name, id: option.product_type_id }} > {option.product_name} </MenuItem>
- 调整
productType的初始状态为对象,更新处理函数:
// 初始状态改为空对象 const [productType, setProductType] = useState({}); const handleProductTypeChange = (event) => { setProductType(event.target.value); };
- 给
Select添加renderValue属性,指定选中时显示的文本:
<Select labelId="demo-simple-select-label" id="demo-simple-select" value={productType} label="Product Type" onChange={handleProductTypeChange} // 自定义选中时显示的内容 renderValue={(selected) => selected.name || ''} >
如果坚持用数组,也需要统一类型:
- 将
productType初始状态改为空数组[] - 处理函数直接保存整个数组:
setProductType(event.target.value); - 添加
renderValue:renderValue={(selected) => selected[0] || ''}
完整修改后的代码示例
import React, { useState, useEffect, Fragment } from 'react'; import { Box, MenuItem, FormControl, Select, InputLabel, CircularProgress, Button } from '@mui/material'; import { createTheme, ThemeProvider } from '@mui/material/styles'; import axios from "axios"; function ProductSelect() { const [loadingData, setLoadingData] = useState(true); // 初始状态改为空对象 const [productType, setProductType] = useState({}); const [productTypes, setProductTypes] = useState([]); const [runmode, setRunmode] = useState('dev'); async function getProducts() { try { const response = await axios.get(`https://myendpoint.myregion.mycloud.com/${ runmode }/product_types`, { headers: { 'Content-Type': 'application/json' } }); const test_data = response.data.data; setProductTypes( test_data.map((x) => ({ product_name: x.product_name, product_type_id: parseInt(x.product_type_id, 10), })) ); setLoadingData(false); } catch (error) { console.error('获取产品类型失败:', error); setLoadingData(false); } } useEffect(() => { getProducts(); // 移除重复调用逻辑,避免重复请求 }, []); const handleProductTypeChange = (event) => { setProductType(event.target.value); }; const theme = createTheme({ palette: { neutral: { main: '#24507C', contrastText: '#fff', }, }, }); return ( <div> <ThemeProvider theme={theme}> {loadingData ? ( <Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', marginBottom: '185px' }}> <CircularProgress/> <span style={{ justifyContent: "center", position: "fixed", top: "30%" }}>Loading. Please wait...</span> </Box> ) : ( <> <FormControl style={{width: '250px', marginTop: '0px', marginBottom: '30px'}}> <InputLabel id="demo-simple-select-label">Product Type</InputLabel> <Select labelId="demo-simple-select-label" id="demo-simple-select" value={productType} label="Product Type" onChange={handleProductTypeChange} renderValue={(selected) => selected.name || ''} > {productTypes.map((option) => ( <MenuItem key={option.product_type_id} value={{ name: option.product_name, id: option.product_type_id }} > {option.product_name} </MenuItem> ))} </Select> </FormControl> </> )} <Button color="neutral" variant="contained" cursor="pointer">Schedule</Button> </ThemeProvider> </div> ); }; export default ProductSelect;
额外优化提示
- 移除了
useEffect中重复调用getProducts的逻辑,避免重复发起请求 - 给axios请求添加了
try/catch,处理请求失败的情况,防止页面一直处于加载状态 - 给
parseInt添加了第二个参数10,确保十进制解析,避免意外问题
内容的提问来源于stack exchange,提问作者gwydion93
相关产品推荐
相关产品推荐

