Material UI Select组件值在选项中却触发越界警告的解决求助
问题场景
编辑页面使用Material UI下拉列表展示部门(选项包含id与name字段),下拉数据通过API获取。从表格选中一条记录后,将记录的departmentName设为下拉框当前值——页面加载时该值显示正常,打开下拉框时匹配选项也会高亮,但始终收到如下警告:
Material-UI: You have provided an out-of-range value
(当前值)for the select (name="departmentName") component. Consider providing a value that matches one of the available options or ''. The available values are ``.
相关代码如下:
表单中的Dropdown组件
<Controls.Dropdown onChange={handleInputChange} name='departmentName' prompt='Select A Department...' url='/api/maintain/departments/' value={values.departmentName} />
Dropdown组件实现
export default function Dropdown(props) { const { error = null, label, onChange, prompt, value, url, ...other } = props; const [options, setOptions] = React.useState([]); React.useEffect(() => { const fetchData = async () => { const res = await axios.get(dropdownUrl); setOptions( res.data ); }; fetchData(); }, [setOptions]); return( <FormControl> <Select defaultValue='' onChange={onChange} value={value === undefined || null ? '' : value} {...other} {...(error && {error: true, helperText: error})} > <MenuItem value=''>{prompt}</MenuItem> {options && options.map((option, id) => ( <MenuItem key={option.id} value={option.name} > {option.name} </MenuItem> ))} </Select> </FormControl> ); }
问题原因
警告触发的核心是组件初始化阶段API还未返回数据,options数组为空,此时传入的value={values.departmentName}不在当前可用选项(仅包含空字符串选项)中,Material UI会判定该值超出范围并抛出警告。后续API返回数据后选项匹配正常,但初始化阶段的不匹配已经触发了警告。
另外原代码存在一处潜在bug:useEffect中使用的dropdownUrl未定义,应该替换为传入的props.url,否则API请求会失败。
解决方法
方法1:添加加载状态,初始化阶段兜底空值
在Dropdown组件中增加加载状态,数据未加载完成时强制下拉框值为空字符串,避免初始化阶段的不匹配:
export default function Dropdown(props) { const { error = null, label, onChange, prompt, value, url, ...other } = props; const [options, setOptions] = React.useState([]); const [isLoading, setIsLoading] = React.useState(true); // 新增加载状态 React.useEffect(() => { const fetchData = async () => { setIsLoading(true); const res = await axios.get(url); // 修正为传入的url setOptions(res.data); setIsLoading(false); }; fetchData(); }, [url]); // 依赖改为url,setOptions不需要作为依赖 // 加载中用空值,加载完成后再使用传入的value const displayValue = isLoading ? '' : (value === undefined || value === null ? '' : value); return( <FormControl> <Select defaultValue='' onChange={onChange} value={displayValue} {...other} {...(error && {error: true, helperText: error})} > <MenuItem value=''>{prompt}</MenuItem> {/* 加载完成后再渲染选项 */} {!isLoading && options.map((option) => ( <MenuItem key={option.id} value={option.name} > {option.name} </MenuItem> ))} </Select> </FormControl> ); }
方法2:校验value是否存在于选项中
在Dropdown组件中直接检查传入的value是否存在于已加载的选项中,不存在则用空值兜底:
export default function Dropdown(props) { const { error = null, label, onChange, prompt, value, url, ...other } = props; const [options, setOptions] = React.useState([]); React.useEffect(() => { const fetchData = async () => { const res = await axios.get(url); setOptions(res.data); }; fetchData(); }, [url]); // 检查value是否在选项列表中,不存在则用空值 const isValidValue = options.some(option => option.name === value); const displayValue = isValidValue ? value : ''; return( <FormControl> <Select defaultValue='' onChange={onChange} value={displayValue} {...other} {...(error && {error: true, helperText: error})} > <MenuItem value=''>{prompt}</MenuItem> {options.map((option) => ( <MenuItem key={option.id} value={option.name} > {option.name} </MenuItem> ))} </Select> </FormControl> ); }
内容的提问来源于stack exchange,提问作者Mtullis

