Adding Validation and Function to Input using React and MUI - reactjs

I am creating a cat APP, that pulls a request from thecatapi, I want my submit button to load my ImageGrid.jsx when they input the correct link for example:
in the dialog box you put:
https://api.thecatapi.com/v1/images/search
then it must have a submit loading function and take you to the images of the cats:
what i have is this:
import React, { useState } from 'react';
import PropTypes from 'prop-types'
import getCat from './ImageGrid'
// Material UI
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogTitle from '#material-ui/core/DialogTitle';
import Input from '#material-ui/core/Input'
import Dialog from '#material-ui/core/Dialog';
import Button from '#material-ui/core/Button';
import Grid from '#material-ui/core/Grid';
const DialogBox = ({ ariaLabel }) => {
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
// const handleGrid = () => {
// React.useState(true)
// }
return (
<div className='modal'>
<Grid container justifyContent='center'>
{/* Button To Open Dialog Box */}
<Button
style={{border: '1px solid #ebc340', color: '#ebc340'}}
variant="outlined"
color="secondary"
onClick={handleClickOpen}>
Welcome to my Case Study, Click to begin
</Button>
</Grid>
{/* Dialog Box Content */}
<Dialog
className='dialog-btn'
open={open}
onClose={handleClose}>
<DialogTitle>
To begin the application, please insert your URL below:
</DialogTitle>
<DialogContent>
<Input
placeholder="Enter Your Link Here"
inputProps={ariaLabel}
fullWidth/>
</DialogContent>
{/* Dialog Box Actions */}
<DialogActions>
<Button
onClick={handleClose}
color="secondary">
Cancel
</Button>
<Button
onClick={ getCat }
color='primary'
autoFocus
type='submit'>
{/* onSubmit={handleGrid} */}
Submit
</Button>
</DialogActions>
</Dialog>
</div>
);
}
Input.inputProps = {
ariaLabel: 'Please insert your URL below',
tertiaryColor: 'tertiary'
}
Input.inputTypes = {
ariaLabel: PropTypes.string.isRequired,
tertiaryColor: PropTypes.string.isRequired,
};
export default DialogBox
and then it needs to load this:
/
/ Fetch data from
import React, { useCallback, useEffect, useState } from "react";
const url = "https://api.thecatapi.com/v1/images/search?limit=9";
function ImageGrid() {
const [catUrls, setCatUrls] = useState();
const getCat = useCallback(() => {
console.log("Hello World");
let catImageUrlList = [];
// fetch http request
fetch(url)
.then((res) => res.json()) //gives data in json
.then((cats) => {
console.log("Cats: ", cats);
for (let i = 0; i < cats.length; i++) {
catImageUrlList.push(cats[i].url);
}
setCatUrls(catImageUrlList);
})
.catch((error) => {
console.log("Error: ", error);
});
}, [setCatUrls]);
useEffect(() => {
console.log("Loading your feline friends....");
getCat();
}, [getCat]);
return (
<>
<h1>Look At These Beautiful Kitty's!</h1>
{catUrls ? (
catUrls.map((catUrl) => <img src={catUrl} alt={"kitty"} />)
) : (
<p>Loading...</p>
)}
<button onClick={getCat}>Refresh</button>
</>
);
}
export default ImageGrid;

Related

ReactJS | Pass Render Contents but ADD onClick

So I'm trying to get some re-use out of my modals that I need to drop in various spots. What I want to do is to separate the BUTTON that opens the modal from the modal itself. This way, the button could be an icon button, a proper button or some other render-able thing. I could be thinking about this wrong too. I maybe, should pass the modal to the button? Not sure, looking for advise. I'm new to ReactJS, so don't assume I know a thing or two, I've made it through a few tutorials so far :D.
Here is a simple current implementation of the two components tied up together. The problem child I think is the usage of the open State.
import React, { useState } from "react";
import {
Button,
Dialog,
DialogContent,
DialogTitle,
DialogContentText,
DialogActions,
IconButton,
} from "#mui/material";
import { Edit } from "#mui/icons-material";
import FacilityForm from "./FacilityForm";
function EditFacilityModal(props) {
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const handleSubmit = async (Facility) => {
let response = await fetch(
props.FacilitiesURL + "/" + String(Facility.ID),
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(Facility),
}
);
if (response.ok) {
}
setOpen(false);
};
return (
<div>
<IconButton
aria-label="expand row"
size="small"
onClick={() => handleClickOpen()}
>
<Edit />
</IconButton>
<Dialog open={open} onClose={handleClose}>
<DialogTitle>Edit Facility</DialogTitle>
<DialogContent>
<DialogContentText>
Please provide the following details to edit the existing facility
in the system.
</DialogContentText>
<FacilityForm
submitActions={handleSubmit}
title="Edit Facility"
details="Facility will be saved exactly as shown."
Facility={props.Facility}
FacilitiesURL={props.FacilitiesURL}
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
</DialogActions>
</Dialog>
</div>
);
}
export default EditFacilityModal;
You could outsource your Dialog into a seperate component which takes open and handleClose as props and other content as props i.e dialogContentText and form as a children prop
function App(props) {
const [openEditFacilityModal, setOpenEditFacilityModal] = React.useState(false);
const handleClickOpen = () => {
setOpenEditFacilityModal(true);
};
const handleClose = () => {
setOpenEditFacilityModal(false);
};
const handleSubmit = async (Facility) => {
let response = await fetch(
props.FacilitiesURL + "/" + String(Facility.ID),
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(Facility),
}
);
if (response.ok) {
}
setOpenEditFacilityModal(false);
};
return <div>
<IconButton
aria-label="expand row"
size="small"
onClick={() => handleClickOpen()}
>
<Edit />
</IconButton>
<EditFacilityModal open={openEditFacilityModal} handleClose={handleClose}>
<FacilityForm
submitActions={handleSubmit}
title="Edit Facility"
details="Facility will be saved exactly as shown."
Facility={props.Facility}
FacilitiesURL={props.FacilitiesURL}
/>
</EditFacilityModal>
</div>;
}
function EditFacilityModal({open, handleClose, dialogContentText, children}) {
return (
<div>
<Dialog open={open} onClose={handleClose}>
<DialogTitle>Edit Facility</DialogTitle>
<DialogContent>
<DialogContentText>
{dialogContentText}
</DialogContentText>
{children}
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
</DialogActions>
</Dialog>
</div>
);
}
export default EditFacilityModal;
You can separate the button that handles the opening of the modal by handling the open state in a top level component.
Here's an example of your component that control the open state. This way you can outsource the modal component and use it anywhere with any button you want.
// MyComponent.jsx
import { Edit } from "#mui/icons-material";
import { IconButton } from "#mui/material";
import React from "react";
import EditFacilityModal from "./EditFacilityModal";
function MyComponent() {
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<>
<IconButton
aria-label="expand row"
size="small"
onClick={() => handleClickOpen()}
>
<Edit />
</IconButton>
<EditFacilityModal
open={open}
handleClose={handleClose}
Facility="Facility"
FacilitiesURL="FacilitiesURL"
/>
</>
);
}
export default MyComponent;
// EditFacilityModal.jsx
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
} from "#mui/material";
import FacilityForm from "./FacilityForm";
function EditFacilityModal(props) {
const handleSubmit = async (Facility) => {
const response = await fetch(
props.FacilitiesURL + "/" + String(Facility.ID),
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(Facility),
}
);
if (response.ok) {
}
props.handleClose();
};
return (
<Dialog open={props.open} onClose={props.handleClose}>
<DialogTitle>Edit Facility</DialogTitle>
<DialogContent>
<DialogContentText>
Please provide the following details to edit the existing facility in
the system.
</DialogContentText>
<FacilityForm
submitActions={handleSubmit}
title="Edit Facility"
details="Facility will be saved exactly as shown."
Facility={props.Facility}
FacilitiesURL={props.FacilitiesURL}
/>
</DialogContent>
<DialogActions>
<Button onClick={props.handleClose}>Cancel</Button>
</DialogActions>
</Dialog>
);
}
export default EditFacilityModal;

How can I pass the value of task id in this example to Chakra UI modal to update the task title?

I am learning to build a to-do app in Nextjs + Typescript. I have a Chakra UI modal which pops up when the user clicks on the "edit task" button. The modal contains a Input element and it should have the value of task title. Here is the solution that I have implemented.
// components/tasks.tsx
import { Box } from '#chakra-ui/react';
import { NextPage } from 'next';
import Task from '../components/task';
import EditTaskModal from '../components/edit-task-modal';
import { useState } from 'react';
import { useModalContext } from '../store/modal';
const Tasks: NextPage = () => {
const { onOpen } = useModalContext();
const [editTaskID, setEditTaskID] = useState('');
const handleOpenModal = (id: string) => {
setEditTaskID(id);
onOpen();
};
return (
<>
<EditTaskModal id={editTaskID} />
<Box>
<Task handleOpenModal={handleOpenModal} />
</Box>
</>
);
};
export default Tasks;
// components/task.tsx
import { NextPage } from 'next';
import { Box, Checkbox, Button, Text } from '#chakra-ui/react';
import { useTaskContext } from '../store/tasks';
import { useModalContext } from '../store/modal';
type TaskProps = {
handleOpenModal: (id: string) => void;
};
const Task: NextPage<TaskProps> = ({ handleOpenModal }) => {
const { tasks } = useTaskContext();
const { onOpen } = useModalContext();
return (
<Box>
{tasks.map((task) => {
return (
<Box
key={task.id}
border="3px solid"
borderColor="gray.200"
marginBottom="1em"
p="1em"
borderRadius="5px"
display="flex"
alignItems="center"
justifyContent="space-between"
>
<Text key={task.id}>{task.title}</Text>
<Box width="35%" display="flex" justifyContent="space-between">
<Button
size="sm"
onClick={() => {
handleOpenModal(task.id);
}}
>
Edit
</Button>
<Button size="sm" colorScheme="red">
Delete
</Button>
</Box>
</Box>
);
})}
</Box>
);
};
export default Task;
// components/edit-task-modal.tsx
import { NextPage } from 'next';
import {
Button,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalCloseButton,
ModalBody,
FormControl,
Input,
ModalFooter,
} from '#chakra-ui/react';
import { useModalContext } from '../store/modal';
import { useEffect, useState } from 'react';
import { useTaskContext } from '../store/tasks';
type EditTaskModalProps = {
id: string;
};
type updateTaskType = { id: string; title: string; status: boolean };
const updateTask = async (task: updateTaskType) => {
try {
const response = await fetch('http://localhost:3000/api/tasks', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(task),
});
} catch (error) {
console.log(error);
}
};
const EditTaskModal: NextPage<EditTaskModalProps> = ({ id }) => {
const { tasks } = useTaskContext();
const task = tasks.find((task) => {
return task.id === id;
});
const [title, setTitle] = useState(task?.title);
const { isOpen, onClose } = useModalContext();
return (
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Update Task Name</ModalHeader>
<ModalCloseButton />
<ModalBody pb={6}>
<FormControl>
<Input
placeholder="Task Name"
value={title}
onChange={(e) => {
setTitle(e.target.value);
}}
/>
</FormControl>
</ModalBody>
<ModalFooter>
<Button
colorScheme="blue"
mr={3}
onClick={() => {
if (task !== undefined)
updateTask({
title: task.title,
status: task.status,
id: task.id,
});
}}
>
Update
</Button>
<Button onClick={onClose}>Cancel</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
};
export default EditTaskModal;
// store/modal.tsx
import React, { createContext, useContext } from 'react';
import { useDisclosure } from '#chakra-ui/react';
const ModalContext = createContext<any>(null);
const ModalProvider = ({ children }: { children: React.ReactNode }) => {
const { onOpen, onClose, isOpen } = useDisclosure();
return (
<ModalContext.Provider value={{ onOpen, onClose, isOpen }}>
{children}
</ModalContext.Provider>
);
};
const useModalContext = () => {
return useContext(ModalContext);
};
export { ModalProvider, useModalContext };
However, I noticed that the value attribute in Input element is always one state update behind for some reason and I can't figure out why. Is there a better solution to pass task id to the modal?

How to prevent re-render when using react material ui dialogs

I'm using modals from the react-material-ui library for a project, and I noticed a side effect when trying to open/close the dialog component. Try this code (code sandbox):
import { useState } from "react";
import moment from "moment";
import Button from "#material-ui/core/Button";
import Dialog from "#material-ui/core/Dialog";
import DialogTitle from "#material-ui/core/DialogTitle";
import DialogActions from "#material-ui/core/DialogActions";
import "./styles.css";
export default function App() {
const [open, setOpen] = useState(false);
const timeStamp = moment().format("HH:mm:ss SS");
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div className="App">
<h1>Mui Dialog</h1>
<h2>Rendered on {timeStamp}</h2>
<Button variant="outlined" color="primary" onClick={handleOpen}>
Open simple dialog
</Button>
<Dialog open={open}>
<DialogTitle>This is a simple dialog</DialogTitle>
<DialogActions>
<Button onClick={handleClose} color="primary" autoFocus>
Close
</Button>
</DialogActions>
</Dialog>
</div>
);
}
You'll see that clicking on the button open will cause a re-render and closing the dialog will have the same effect, it's normal because state changed after calling hooks. This is often undesirable, I don't want to re-render the page when opening or after closing.
So I tried to use the following solution, based of refs (code sandbox):
import { useState, useRef, forwardRef, useImperativeHandle } from "react";
import moment from "moment";
import Button from "#material-ui/core/Button";
import Dialog from "#material-ui/core/Dialog";
import DialogTitle from "#material-ui/core/DialogTitle";
import DialogActions from "#material-ui/core/DialogActions";
import "./styles.css";
const SimpleDialog = forwardRef(({ title }, ref) => {
const [open, setOpen] = useState(false);
const innerRef = useRef();
const handleClose = () => {
setOpen(false);
};
useImperativeHandle(ref, () => ({
openDialog: () => setOpen(true),
closeDialog: () => setOpen(false)
}));
return (
<Dialog open={open} ref={innerRef}>
<DialogTitle>{title}</DialogTitle>
<DialogActions>
<Button onClick={handleClose} color="primary" autoFocus>
Close
</Button>
</DialogActions>
</Dialog>
);
});
export default function App() {
const timeStamp = moment().format("HH:mm:ss SS");
const dialogRef = useRef();
const handleOpen = () => {
dialogRef.current.openDialog();
};
return (
<div className="App">
<h1>Mui Dialog</h1>
<h2>Rendered on {timeStamp}</h2>
<Button variant="outlined" color="primary" onClick={handleOpen}>
Open simple dialog
</Button>
<SimpleDialog ref={dialogRef} title="This is a simple dialog" />
</div>
);
}
My question is: is this a correct approach to solve the problem of unwanted re-renders in the case of react-material-ui modals ?
Regards.

How do I remove black borders on a pop-up box?

I am using material UI and I think the default import of component has black borders. I want to make it so the box is all white with no black borders. Is there a code snippet I can addon to my code to make the black borders go away?
Current outcome
Desired outcome, see how there are no black borders?
code
import { Avatar, IconButton } from '#material-ui/core';
import React, { useEffect, useState } from 'react';
import './Sidebar.css';
import SearchIcon from '#material-ui/icons/Search';
import { RateReviewOutlined } from '#material-ui/icons';
import { SidebarChat } from './SidebarChat';
import { useSelector } from 'react-redux';
import { selectUser } from './features/userSlice';
import db, { auth } from './firebase';
import { makeStyles } from '#material-ui/core/styles';
import Modal from '#material-ui/core/Modal';
import Backdrop from '#material-ui/core/Backdrop';
import Fade from '#material-ui/core/Fade';
import Card from '#material-ui/core/Card';
import CardActionArea from '#material-ui/core/CardActionArea';
import CardActions from '#material-ui/core/CardActions';
import CardContent from '#material-ui/core/CardContent';
import Button from '#material-ui/core/Button';
import Typography from '#material-ui/core/Typography';
const useStyles = makeStyles((theme) => ({
modal: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
root: {
maxWidth: 345,
},
media: {
height: 140,
},
}));
export function Sidebar(props) {
const user = useSelector(selectUser);
const [chats, setChats] = useState([]);
const [filterChats, setFilteredChats] = useState([]);
const classes = useStyles();
const [search, setSearch] = useState('');
const [open, setOpen] = useState(false);
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
useEffect(() => {
db.collection('chats').onSnapshot((snapshot) =>
setChats(
snapshot.docs.map((doc) => ({
id: doc.id,
data: doc.data(),
}))
)
);
db.collection('chats').onSnapshot((snapshot) =>
setFilteredChats(
snapshot.docs.map((doc) => ({
id: doc.id,
data: doc.data(),
}))
)
);
}, []);
const addChat = () => {
const chatName = prompt('Please enter a chat name');
if (chatName) {
db.collection('chats').add({
chatName: chatName,
});
}
};
const searchFunction = (e) => {
setSearch(e);
const filtered = chats.filter(chat => {
return chat.data.chatName.toLowerCase().includes(e.toLowerCase())
});
setFilteredChats(filtered);
};
return (
<div className="sidebar">
<div className="sidebar__header">
<Avatar
onClick={handleOpen}
src={user.photo}
className="sidebar__avatar"
/>
<div className="sidebar__input">
<SearchIcon />
<input
placeholder="Search"
value={search}
onChange={(e) => searchFunction(e.target.value)}
/>
</div>
<IconButton variant="outlined" className="sidebar__inputButton">
<RateReviewOutlined onClick={addChat} />
</IconButton>
</div>
<div className="sidebar__chats">
{filterChats.map(({ id, data: { chatName } }) => (
<SidebarChat key={id} id={id} chatName={chatName} />
))}
</div>
<Modal
style={{border: '2px solid #FFFFFF',}}
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
className={classes.modal}
open={open}
onClose={handleClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={open}>
<Card className={classes.root}>
<CardActionArea>
<CardContent>
<Typography gutterBottom variant="h5" component="h2">
{user.displayName.split(' ').slice(0,1).join(' ')},
</Typography>
<Typography variant="body2" color="textSecondary" component="p">
Are you sure you want to sign out?
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Button onClick={() => auth.signOut()} size="small" color="primary">
Yes
</Button>
<Button onClick={handleClose} size="small" color="primary">
No
</Button>
</CardActions>
</Card>
</Fade>
</Modal>
</div>
);
}
answer below - switched material ui from Modal to Dialog
import { Avatar, IconButton } from '#material-ui/core';
import React, { useEffect, useState } from 'react';
import './Sidebar.css';
import SearchIcon from '#material-ui/icons/Search';
import { RateReviewOutlined } from '#material-ui/icons';
import { SidebarChat } from './SidebarChat';
import { useSelector } from 'react-redux';
import { selectUser } from './features/userSlice';
import db, { auth } from './firebase';
import { makeStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogContentText from '#material-ui/core/DialogContentText';
import DialogTitle from '#material-ui/core/DialogTitle';
export function Sidebar(props) {
const user = useSelector(selectUser);
const [chats, setChats] = useState([]);
const [filterChats, setFilteredChats] = useState([]);
const [search, setSearch] = useState('');
const [open, setOpen] = useState(false);
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
useEffect(() => {
db.collection('chats').onSnapshot((snapshot) =>
setChats(
snapshot.docs.map((doc) => ({
id: doc.id,
data: doc.data(),
}))
)
);
db.collection('chats').onSnapshot((snapshot) =>
setFilteredChats(
snapshot.docs.map((doc) => ({
id: doc.id,
data: doc.data(),
}))
)
);
}, []);
const addChat = () => {
const chatName = prompt('Please enter a chat name');
if (chatName) {
db.collection('chats').add({
chatName: chatName,
});
}
};
const searchFunction = (e) => {
setSearch(e);
const filtered = chats.filter(chat => {
return chat.data.chatName.toLowerCase().includes(e.toLowerCase())
});
setFilteredChats(filtered);
};
return (
<div className="sidebar">
<div className="sidebar__header">
<Avatar
onClick={handleOpen}
src={user.photo}
className="sidebar__avatar"
/>
<div className="sidebar__input">
<SearchIcon />
<input
placeholder="Search"
value={search}
onChange={(e) => searchFunction(e.target.value)}
/>
</div>
<IconButton variant="outlined" className="sidebar__inputButton">
<RateReviewOutlined onClick={addChat} />
</IconButton>
</div>
<div className="sidebar__chats">
{filterChats.map(({ id, data: { chatName } }) => (
<SidebarChat key={id} id={id} chatName={chatName} />
))}
</div>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{`${user.displayName.split(' ').slice(0,1).join(' ')},`}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Are you sure you want to sign out?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={() => auth.signOut()} color="primary">
Yes
</Button>
<Button onClick={handleClose} color="primary" autoFocus>
No
</Button>
</DialogActions>
</Dialog>
</div>
);
}

ReactJS Modal with Material UI

I'm trying to make a reusable confirmation modal with Material UI but when I press CANCEL or OK the modal does not close.
The code is here:
https://codesandbox.io/s/lucid-hoover-sput6?file=/src/App.js
I can't figure it out why the pop don't dissapear.
LE: added the code here so it remains
ConfirmModal.js
import React from "react";
import { Button } from "#material-ui/core";
import Dialog from "#material-ui/core/Dialog";
import DialogActions from "#material-ui/core/DialogActions";
import DialogContent from "#material-ui/core/DialogContent";
import DialogContentText from "#material-ui/core/DialogContentText";
const ConfirmModal = (props) => {
const { content, open, setOpen, onConfirm } = props;
return (
<Dialog
open={open}
onClose={() => setOpen(false)}
aria-labelledby="dialog-title"
>
<DialogContent>
<DialogContentText>{content}</DialogContentText>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={() => setOpen(false)} color="primary">
Cancel
</Button>
<Button
onClick={() => {
setOpen(false);
onConfirm();
}}
color="primary"
>
OK
</Button>
</DialogActions>
</Dialog>
// </div>
);
};
export default ConfirmModal;
App.js
import React, { useState } from "react";
import { IconButton } from "#material-ui/core";
import { Input as InputIcon } from "#material-ui/icons";
import ConfirmModal from "./ConfirmModal";
export default function App() {
const [confirmOpen, setConfirmOpen] = useState(false);
const handleLogout = () => {
console.log("this hould logout the user");
};
return (
<div className="App">
<h2>Press the button below so the confirmation modal appears </h2>
<IconButton color="inherit" onClick={() => setConfirmOpen(true)}>
<InputIcon />
<ConfirmModal
content="Are you sure you want to leeeave us ?"
open={confirmOpen}
setOpen={setConfirmOpen}
onConfirm={handleLogout}
/>
</IconButton>
</div>
);
}
Move the modal out of the button. The Modal's cancel/confirm/backdrop click events are propagating (bubbling) up to the open button (IconButton) and its onClick handler is just reopening the modal by setting confirmOpen state true.
export default function App() {
const [confirmOpen, setConfirmOpen] = useState(false);
const handleLogout = () => {
console.log("this hould logout the user");
};
return (
<div className="App">
<h2>Press the button below so the confirmation modal appears </h2>
<IconButton color="inherit" onClick={() => setConfirmOpen(true)}>
<InputIcon />
</IconButton>
<ConfirmModal
content="Are you sure you want to leeeave us ?"
open={confirmOpen}
setOpen={setConfirmOpen}
onConfirm={handleLogout}
/>
</div>
);
}

Resources