React自定义SelectOption组件引发无限循环问题求助
React表单无限循环问题解析
问题场景
开发React表单时遭遇无限循环异常,表单使用了自定义SelectOption组件,移除该组件后控制台的无限循环现象消失。相关代码如下:
Form组件代码
import { SelectOption } from "../../../component/select/SelectOption" const Form = () => { const [selectState, setSelectState] = useState(0); const [selectCity, setSelectCity] = useState(0); const [listStates, setListStates] = useState([{}]); const [listCity, setListCity] = useState([{}]); const loadListStates = () => { axios.request({ /*configuration request with token*/ }) .then((response) => { const newStruct = response.data.map((e)=>{ return { 'id': e.id, 'name': e.fullName }}); setListStates( newStruct ) }) .catch((error) => { }); } const loadListCity = () => { axios.request({ /*configuration request with token*/ }) .then((response) => { const newStruct = response.data.map( (e) => {return { 'id': e.id, 'name': e.fullName }} ); setListCity( newStruct ); }) .catch((error) => { }); } loadListStates(); loadListCity(); return( <Div> <SelectDiv> <SelectOption key={1} value={selectState} setValue={setSelectState} text="Select one" list={listStates} borderRadious="10px" width="20rem" /> <SelectOption key={1} value={selectCity} setValue={setSelectCity} text="Select one" list={listCity} borderRadious="10px" width="20rem" /> </SelectDiv> </Div> ) } export default Form;
SelectOption组件代码
import { useState } from 'react'; import MenuItem from '@mui/material/MenuItem'; import FormControl from '@mui/material/FormControl'; import Select from '@mui/material/Select'; import { Colors } from '../../const/colors'; import { Fonts } from "../../const/fonts"; export const SelectOption = ({ key, value, setValue, text, list, width, borderRadious}) => { const handleChange = (event) => { setValue(event.target.value); }; return( <FormControl> <Select key={key} value={value} onChange={(e)=>handleChange(e)} displayEmpty inputProps={{ 'aria-label': 'Without label' }} defaultValue="" style={{ textDecoration: 'none', width: width? width : '200px', height: '40px', top: '0.31px', borderRadius: borderRadious? borderRadious : '5px', border: '0', padding: '11px, 20px, 11px, 20px', backgroundColor: Colors.backgroundElement, font: Fonts.Poppins }} MenuProps={{ PaperProps: { sx: { "& .MuiMenuItem-root:hover": { opacity: 1 }, "& .MuiMenuItem-root.Mui-selected": { opacity: 1 }, "& .MuiMenuItem-root": { backgroundColor: Colors.backgroundElement, font: Fonts.Poppins, opacity: 0.5 }, "& .css-6hp17o-MuiList-root-MuiMenu-list":{ backgroundColor: Colors.backgroundElement } } } }} > <MenuItem defaultValue="" key={0}> {text} </MenuItem> { list?.map((value)=>{ return( <MenuItem key={value.id} value={value.id} > {value.name} </MenuItem> ) }) } </Select> </FormControl> ) }
无限循环核心原因
请求函数无限制触发重渲染:在Form组件的顶层直接调用
loadListStates()和loadListCity(),组件每次渲染都会执行这两个函数。请求完成后调用setListStates/setListCity更新状态,状态变更又会触发组件重新渲染,形成渲染→请求→更新状态→再渲染的无限循环。重复Key引发组件识别混乱:两个
SelectOption组件的key都设为1,React依靠key区分组件实例,重复key会导致组件复用逻辑出错,可能触发额外的不必要渲染,加剧循环问题。受控组件混用非受控属性:SelectOption中的
<Select>同时设置了value={value}(受控模式)和defaultValue=""(非受控模式),这会导致组件内部状态与外部传入状态冲突,触发额外的渲染更新。
修复方案
- 用useEffect包裹请求逻辑:确保请求仅在组件挂载时执行一次,避免每次渲染都触发:
// 替换Form组件中直接调用loadListStates和loadListCity的代码 import { useEffect } from 'react'; // 别忘了导入useEffect // ...其他代码 useEffect(() => { loadListStates(); loadListCity(); }, []); // 空依赖数组表示仅在组件挂载时执行一次
- 设置唯一的组件Key:给每个
SelectOption分配唯一key,帮助React正确识别组件:
<SelectOption key="state-select" // ...其他props /> <SelectOption key="city-select" // ...其他props />
- 移除受控组件的defaultValue:既然
<Select>通过value和onChange设置为受控组件,删除defaultValue=""属性,避免状态冲突:
// 修改SelectOption组件中的Select标签 <Select key={key} value={value} onChange={(e)=>handleChange(e)} displayEmpty inputProps={{ 'aria-label': 'Without label' }} // 移除defaultValue="" style={/* ...原有样式 */} // ...其他props >
内容的提问来源于stack exchange,提问作者alex
相关产品推荐
相关产品推荐

