React MUI Select组件renderValue选中后不更新问题求助
问题:MUI Select切换选项时renderValue不更新,始终显示初始选中标签
我开发了ClientGoalReport组件,MUI Select初始值为空,数据加载后通过defaultPlan状态设置选中值,菜单标签显示正常,但切换选中项时,renderValue不更新,始终显示defaultPlan的标签,实际选中值(planId/isStart/isEnd)已正常变更。尝试过给Select加状态key、监听状态的useEffect、在handleChange中调用renderValue,都没解决问题。
组件代码
const ClientGoalReport = () => { const { clientId } = useParams(); const [ planId, setPlanId ] = useState() const [ planList, setPlanList ] = useState([]) const [ defaultPlan, setDefaultPlan ] = useState('') const [ isStart, setIsStart ] = useState() const [ isEnd, setIsEnd ] = useState() const [ header, setHeader ] = useState() const [tableData, setTableData] = useState() const [hasData, setHasData] = useState('initial') const [renderKey, setRenderKey] = useState(0); const { data: plans } = useGetAllClientReportsQuery(clientId) useEffect(() => { if (plans) { let list = plans.map((plan, idx) => ( { value: {start: plan.startDate, end: plan.endDate, planId: plan._id}, label: `${idx+1} ${plan.type} Report Date: ${new Date(plan.reportDate).toLocaleDateString()}, Start: ${new Date(plan.startDate).toLocaleDateString()}, End: ${new Date(plan.endDate).toLocaleDateString()} ` } )).sort((a, b) => a.startDate - b.startDate) setPlanList(list); const def = list[list.length -1] setDefaultPlan(def) } }, [plans]) const [pullData, { data, isSuccess }] = useGetGoalAttainDataMutation() const generatePlanOptions = () => { return planList.map((plan, idx) => { return ( <MenuItem key={idx} value={plan.value}> {plan.label} </MenuItem> ) }) } const renderValue = (selected) => { if (!selected) return ''; return selected.label; } const handlePlanChange = (event) => { setRenderKey((prevKey) => prevKey + 1); setPlanId(event.target.value.planId) setIsStart(event.target.value.start) setIsEnd(event.target.value.end) } useEffect(() => { //takes data and constructs table to display }, [data, isSuccess, isStart, isEnd]) return ( <> <div display='flex'> <Box sx={{ display: 'flex', flexDirection: 'row', p: 1, m: 1, borderRadius: 1, }} > <h1>Search by Service Plan</h1><br /> <FormControl sx={{ m: 1, minWidth: 120 }}> <InputLabel id="plan-select-label">Plan</InputLabel> <Select labelId="plan-select-label" label="Plan" name='plan' value={defaultPlan} onChange={handlePlanChange} autoWidth key={renderKey} renderValue={renderValue} native={false} displayEmpty={true} > {generatePlanOptions()} </Select> </FormControl><br /> <Button color='primary' variant='contained' onClick={() => pullData({clientId, isStart, isEnd, planId})}> Run </Button> </Box> </div> {displayReport} </> ) }
问题根源
Select组件的value绑定的是defaultPlan状态,但handlePlanChange中仅更新了planId/isStart/isEnd,从未更新defaultPlan。renderValue接收的selected参数就是value绑定的状态值,所以始终显示初始的defaultPlan.label,而实际选中的选项数据(event.target.value)并没有关联到Select的显示状态上。
修复方案
- 添加专门的
selectedPlan状态来跟踪当前选中的选项,替代直接用defaultPlan作为Select的value - 在
handlePlanChange中,根据选中的value从planList中找到对应的完整选项对象,更新selectedPlan - 移除不必要的
renderKey,状态正常更新后Select会自动重渲染
修改后的关键代码片段
const ClientGoalReport = () => { // ... 其他状态保持不变 const [ selectedPlan, setSelectedPlan ] = useState(null); // 新增选中状态 useEffect(() => { if (plans) { let list = plans.map((plan, idx) => ( { value: {start: plan.startDate, end: plan.endDate, planId: plan._id}, label: `${idx+1} ${plan.type} Report Date: ${new Date(plan.reportDate).toLocaleDateString()}, Start: ${new Date(plan.startDate).toLocaleDateString()}, End: ${new Date(plan.endDate).toLocaleDateString()} ` } )).sort((a, b) => a.startDate - b.startDate) setPlanList(list); const def = list[list.length -1] setDefaultPlan(def); setSelectedPlan(def); // 初始化选中状态 } }, [plans]) const handlePlanChange = (event) => { const selectedValue = event.target.value; // 从planList中找到对应的完整选项对象 const matchedPlan = planList.find(plan => plan.value.planId === selectedValue.planId && plan.value.start === selectedValue.start && plan.value.end === selectedValue.end ); setSelectedPlan(matchedPlan); // 更新选中状态 setPlanId(selectedValue.planId); setIsStart(selectedValue.start); setIsEnd(selectedValue.end); } return ( // ... 其他渲染内容保持不变 <Select labelId="plan-select-label" label="Plan" name='plan' value={selectedPlan} // 绑定到选中状态 onChange={handlePlanChange} autoWidth renderValue={renderValue} native={false} displayEmpty={true} > {generatePlanOptions()} </Select> // ... 其他渲染内容保持不变 ) }
补充说明
- 因为
MenuItem的value是plan.value(一个对象),而selectedPlan是包含value和label的完整对象,所以Select内部可以正确匹配选中项 - 如果担心对象比较的问题,可以给每个plan添加唯一标识(比如
plan._id),在find时只对比唯一标识,更高效准确 - 移除
renderKey是因为状态更新后组件会正常触发重渲染,不需要强制刷新
内容的提问来源于stack exchange,提问作者Jowz
相关产品推荐
相关产品推荐

