MUI多选选择器:实现多选时不关闭下拉菜单,且完成选择后再提交选中项数组
MUI多选选择器:实现多选时不关闭下拉菜单,且完成选择后再提交选中项数组
看起来你已经在MUI的多选Select组件上搭好了基础框架,现在的核心需求是从「选一个月份就立即更新父组件的选中数组」,改成「让用户选完所有想要的月份后,再一次性提交选中项数组」对吧?我来帮你调整代码解决这个问题~
问题根源分析
你原来的代码里,handleChange直接调用了父组件传过来的setSelectedMonths,所以每选一个选项就会立即更新父组件状态。我们需要新增一个本地临时状态来存储用户的实时选择,等用户确认后再同步给父组件,同时保留多选时菜单不关闭的逻辑。
修改后的完整代码
import { useState, useRef } from 'react'; import { FormControl, InputLabel, Select, OutlinedInput, MenuItem, Checkbox, ListItemText, Box, Button } from '@mui/material'; const MonthPicker = ({ monthsList, selectedMonths, setSelectedMonths }) => { // 新增本地临时状态,实时存储用户选择,不直接触发父组件更新 const [tempSelectedMonths, setTempSelectedMonths] = useState(selectedMonths); // 用来获取菜单实例,方便点击确认后关闭菜单 const menuListRef = useRef(null); // 处理选项选择,更新临时状态 const handleChange = (event) => { const { target: { value } } = event; setTempSelectedMonths(typeof value === "string" ? value.split(",") : value); }; // 确认选择,同步临时状态到父组件,并关闭菜单 const handleConfirmSelection = () => { setSelectedMonths([...tempSelectedMonths]); // 关闭下拉菜单 if (menuListRef.current) { const menuElement = menuListRef.current.closest('[role="menu"]'); if (menuElement) menuElement.style.display = 'none'; } }; const MenuProps = { disableAutoFocusItem: true, // 给菜单添加ref,方便后续关闭 MenuListProps: { ref: menuListRef, // 禁止菜单点击空白处关闭(可选,根据需求调整) onMouseDown: (e) => e.stopPropagation(), }, }; return ( <FormControl sx={{ width: "100%", "& .MuiInputLabel-root": { fontSize: "1rem", color: Colors.dark, }, "& .MuiSelect-root": { fontSize: "0.8rem", }, }} size="small" > <InputLabel id="month-picker-label">Oyni Tanlash</InputLabel> <Select multiple labelId="month-picker-label" id="month-picker" value={tempSelectedMonths} onChange={handleChange} input={<OutlinedInput label="Oyni Tanlash" />} renderValue={(selected) => selected.join(", ")} MenuProps={MenuProps} > {monthsList.map((month) => ( <MenuItem key={month.id} value={month.name} sx={{ padding: "4px 8px", display: "flex", alignItems: "center", gap: "8px", }} // 阻止点击选项时关闭菜单 onClick={(event) => event.stopPropagation()} > <Checkbox checked={tempSelectedMonths.includes(month.name)} sx={{ color: Colors.blue_nbu, padding: 0 }} size="small" /> <ListItemText primary={month.name} sx={{ fontSize: "0.8rem", margin: 0 }} /> </MenuItem> ))} {/* 添加确认与取消按钮 */} <MenuItem sx={{ borderTop: "1px solid #eee", justifyContent: "flex-end", padding: "8px 16px", }} onClick={(e) => e.stopPropagation()} > <Box sx={{ display: "flex", gap: "8px" }}> <Button size="small" variant="outlined" onClick={() => { // 重置临时选择并关闭菜单 setTempSelectedMonths(selectedMonths); const menuElement = menuListRef.current?.closest('[role="menu"]'); if (menuElement) menuElement.style.display = 'none'; }} > Bekor qilish </Button> <Button size="small" variant="contained" onClick={handleConfirmSelection} sx={{ backgroundColor: Colors.blue_nbu }} > Tasdiqlash </Button> </Box> </MenuItem> </Select> </FormControl> ); };
关键改动说明
- 本地临时状态:新增
tempSelectedMonths来实时存储用户的选择,用户选选项时只会更新本地状态,不会触发父组件的状态更新,实现“暂存选择”的效果。 - 确认按钮逻辑:在菜单底部添加了“确认”和“取消”按钮,点击“确认”才会把临时选择同步给父组件的
selectedMonths,点击“取消”则重置临时状态并关闭菜单。 - 菜单关闭控制:用
useRef获取菜单实例,点击按钮后手动关闭菜单,同时保留了原来的event.stopPropagation()来防止选选项时菜单意外关闭。 - 初始状态同步:初始化临时状态时绑定父组件传的
selectedMonths,保证编辑场景下能正确显示已选中的内容。
这样调整后,用户就可以在下拉菜单里自由选择多个月份,选完后点击确认才会提交选中的数组,完全符合你的需求啦~
备注:内容来源于stack exchange,提问作者Ogabek Otaxonov
相关产品推荐
相关产品推荐

