I am trying to create a dynamic modal with React and MaterialUI.
Currently adding them like this:
// Confirm.js
import { WarningAmber } from '@mui/icons-material'
import { LoadingButton } from '@mui/lab'
import { Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from '@mui/material'
import { t } from 'i18next'
import { useState } from 'react'
export default function Confirm({ open, setOpen, title, message, onConfirm, onCancel = () => null }) {
const [saving, setSaving] = useState(false)
function handleCancel() {
onCancel()
setOpen(false)
}
function handleConfirm() {
setSaving(true)
onConfirm()
setSaving(false)
setOpen(false)
}
return (
<Dialog onClose={() => setOpen(false)} open={open}>
<DialogTitle className="flex items-center">
<WarningAmber className="mr-2" />
{title}
</DialogTitle>
<DialogContent>
<DialogContentText>{message}</DialogContentText>
</DialogContent>
<DialogActions>
<Button className="mx-2" variant="text" onClick={handleCancel}>
{t('cancel')}
</Button>
<LoadingButton loading={saving} variant="contained" onClick={handleConfirm}>
{t('yes')}
</LoadingButton>
</DialogActions>
</Dialog>
)
}
// in any component
export default function SomeTable() {
const [confirmDelete, setConfirmDelete] = useState(false)
const [something, setSomething] = useState(null)
// triggered by a button on table
function handleDelete(item) {
setConfirmDelete(true)
setSomething(item)
}
async function deleteSomething() {
// delete
}
return (
<>
{*
table
*}
<Confirm
open={confirmDelete}
setOpen={setConfirmDelete}
title="are-you-sure"
message="delete-confirm-message"
onConfirm={deleteSomething}
/>
</>
);
}
I dont want to add the Confirm component to every other component like this.
Instead I want this:
// useDialog.js
import { useState } from "react";
import { createPortal } from "react-dom";
import Confirm from "../components/common/Confirm";
export default function useConfirm() {
const [open, setOpen] = useState(true);
return {
confirm: ({ title, message, onConfirm, onCancel = () => null }) =>
createPortal(
<Confirm
open={open}
setOpen={setOpen}
title={title}
message={message}
onConfirm={onConfirm}
onCancel={onCancel}
/>,
document.getElementById("root")
)
};
}
// any component
import useConfirm from "./hooks/useConfirm";
import "./styles.css";
import { Button } from "@mui/material";
export default function App() {
const { confirm } = useConfirm();
return (
<div className="App">
<Button
onClick={() =>
confirm({
title: "Modal Title",
message: "Modal Message",
onConfirm: () => null
})
}
>
Open Confirm Modal
</Button>
</div>
);
}
But when I do it like this, createPortal is not doing anything, not throwing any errors either.
Here is the sandbox https://codesandbox.io/s/modest-saha-hjvgwz?file=/src/App.js
2
Answers
Followed @ArsanyBenyamine's comment and got it to working by converting it to context. I added Confirm component globally and controlled its state in in the context. Here is the final sandbox https://codesandbox.io/p/devbox/modest-saha-hjvgwz
please update your
useDialog.js
file with below code. I hope it will work.