You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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=""(非受控模式),这会导致组件内部状态与外部传入状态冲突,触发额外的渲染更新。

修复方案

  1. 用useEffect包裹请求逻辑:确保请求仅在组件挂载时执行一次,避免每次渲染都触发:
// 替换Form组件中直接调用loadListStates和loadListCity的代码
import { useEffect } from 'react'; // 别忘了导入useEffect

// ...其他代码

useEffect(() => {
  loadListStates();
  loadListCity();
}, []); // 空依赖数组表示仅在组件挂载时执行一次
  1. 设置唯一的组件Key:给每个SelectOption分配唯一key,帮助React正确识别组件:
<SelectOption    
  key="state-select"
  // ...其他props
/> 

<SelectOption    
  key="city-select"
  // ...其他props
/> 
  1. 移除受控组件的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 18:44:52