for MUI learning purposes I'm creating a simple CRUD app with a modal. That modal contains a simple form with a few TextField and one Select components. THe issue is, that when clicking on the Select component, the modal closes.
Modal:
<ClickAwayListener
onClickAway={handleClickAway}
>
<Box sx={{ marginTop: '80px' }}>
<Button
sx={{
borderRadius: '8px',
backgroundColor: '#fff',
color: '#091fbb',
border: '1px solid #091fbb'
}}
onClick={handleOpen}
>
Add new
</Button>
<Modal
hideBackdrop
open={open}
onClose={handleClose}
sx={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
backgroundColor: '#fff',
border: '1px solid #b9c2ff',
borderRadius: '8px',
height: 'fit-content',
width: 400,
boxShadow: 2,
}}
>
<form
onSubmit={handleSubmit}
style={{
display: 'flex',
flexDirection: 'column',
paddingTop: '12px',
paddingLeft: '18px',
paddingRight: '18px',
paddingBottom: '30px',
}}
>
<Typography variant='h6' sx={{ my: 2, textAlign: 'center' }}>ADD NEW PARTICIPANT</Typography>
<FormControl sx={{ my: 1 }}>
<Typography variant='body2'>Fullname</Typography>
<TextField
variant='standard'
value={fullname}
onChange={(e) => setFullname(e.target.value)}
/>
</FormControl>
<FormControl sx={{ my: 1 }}>
<Typography variant='body2'>Gender</Typography>
<Select
variant='standard'
value={gender}
MenuProps={{
onClick: e => {
e.preventDefault();
}
}}
onChange={(e) => setGender(e.target.value)}
>
<MenuItem value="None"><em>None</em></MenuItem>
<MenuItem value='Male'>Male</MenuItem>
<MenuItem value='Female'>Female</MenuItem>
<MenuItem value='Other'>Other</MenuItem>
</Select>
</FormControl>
<FormControl sx={{ my: 1 }}>
<Typography variant='body2'>Email</Typography>
<TextField
variant='standard'
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</FormControl>
<FormControl sx={{ my: 1 }}>
<Typography variant='body2'>Phone nr</Typography>
<TextField
variant='standard'
value={phone}
onChange={(e) => setPhone(e.target.value)}
/>
</FormControl>
<FormControl sx={{ my: 1 }}>
<Typography variant='body2'>Description</Typography>
<TextField
variant='standard'
value={description}
onChange={(e) => setDescription(e.target.value)}
multiline
rows={3}
/>
</FormControl>
{ !isLoading && <Button
variant='contained'
type='submit'
sx={{
backgroundColor: '#091fbb'
}}>
Add participant
</Button>}
{ isLoading && <Button
variant='contained'
type='submit'
disabled
sx={{
backgroundColor: '#091fbb'
}}>
Adding participant...
</Button>}
</form>
</Modal>
</Box>
</ClickAwayListener>
Handler functions and states for Modal:
const [open, setOpen] = useState(false);
const [fullname, setFullname] = useState('');
const [gender, setGender] = useState('None');
const [email, setEmail] = useState('');
const [phone, setPhone] = useState('');
const [description, setDescription] = useState('');
const [isLoading, setIsLoading] = useState(false);
const handleOpen = () => {
setOpen(!open);
};
const handleClose = () => {
setFullname('');
setGender('None');
setEmail('');
setPhone('');
setDescription('');
setOpen(false);
};
const handleClickAway = (e) => {
if (!e.target.classList.contains('MuiMenuItem-root')) {
setFullname('');
setGender('None');
setEmail('');
setPhone('');
setDescription('');
setOpen(false);
}
};
const handleSubmit = (e) => {
e.preventDefault();
const newParticipant = { fullname, gender, email, phone, description };
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newParticipant)
};
setIsLoading(true);
fetch('http://localhost:8000/participants', requestOptions)
.then(() => {
setFullname('');
setGender('None');
setEmail('');
setPhone('');
setDescription('');
setIsLoading(false);
setOpen(!open);
})
};
Could anyone advise on how to solve this? Adding MenuProps to prevent default behavior on the Select component and the if statement in handleClickAway function didnt help in my case, even though that helped other who were facing the same issue.
Assuming that the goal is to have Select
work in Modal
without closing it, perhaps the default behavior of Modal
could be enough and use of ClickAwayListener
may be not be necessary.
Instead of styling Modal
directly with the sx
prop, try wrap the modal content in a Box
and style this container. This preserves the default behavior of Modal
, so that clicking on Select
would not trigger the closing of it.
Since Modal
internally detect click on the backdrop to close itself, consider to style the backdrop with a transparent background instead of disabling it, so that the use of ClickAwayListener
could also be omitted.
Demo of simplified example on:stackblitz (excluded all data handling)
<Modal
open={open}
onClose={handleClose}
// 👇 Style the backdrop to be transparent
slotProps={{ backdrop: { sx: { background: "transparent" } } }}
>
<Box
// 👇 Style the container Box for modal content
sx={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
backgroundColor: "#fff",
border: "1px solid #b9c2ff",
borderRadius: "8px",
height: "fit-content",
width: 400,
boxShadow: 2,
}}
>
{/* Modal content here */}
</Box>
</Modal>
This happens because the menu is by default mounted in the DOM outside of the modal HTML hierarchy; the Select
component uses a Menu
component which in turn uses a Popper
component. Looking at the API documentation for Popper
:
The children will be under the DOM hierarchy of the parent component.
disablePortal:bool = false
A simple solution is to override this default in MenuProps
, which will cause the component to be rendered as a child to the Modal and will no longer trigger the ClickAwayListener
callback. I'm not aware of any downsides to this approach.
<Select
variant='standard'
value={gender}
MenuProps={{
disablePortal: true, // <--- HERE
onClick: e => {
e.preventDefault();
}
}}
onChange={(e) => setGender(e.target.value)}
> . . . </Select>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.