Material ui dialog closes the snackbar along with it.
This is a weird problem so I created a demo to demonstrate the issue:
I am passing states from parent to child so the parent state can update based on the child
<Child openDialog={openDialog} setOpenDialog={setOpenDialog} />
In the child, I am using these as below
export default function Child({openDialog, setOpenDialog}) {
The button in the child is supposed to close only the dialog but it closes snackbar too.
<button
onClick={() => dialogClick()}
>Click this dialog button
</button>
Line 12 in the child has setOpenSnack("true");
This only works when I ment out line 13 setOpenDialog(false);
const dialogClick = (event) => {
setMsg("This should close dialog only and open the snackbar");
setOpenSnack(true);
setOpenDialog(false);
};
This behavior is only when I split it into child and parent.
In short, why does setOpenSnack(true);
in the child not work?
Material ui dialog closes the snackbar along with it.
This is a weird problem so I created a demo to demonstrate the issue:
https://codesandbox.io/s/react-hooks-counter-demo-v20w3
I am passing states from parent to child so the parent state can update based on the child
<Child openDialog={openDialog} setOpenDialog={setOpenDialog} />
In the child, I am using these as below
export default function Child({openDialog, setOpenDialog}) {
The button in the child is supposed to close only the dialog but it closes snackbar too.
<button
onClick={() => dialogClick()}
>Click this dialog button
</button>
Line 12 in the child has setOpenSnack("true");
This only works when I ment out line 13 setOpenDialog(false);
const dialogClick = (event) => {
setMsg("This should close dialog only and open the snackbar");
setOpenSnack(true);
setOpenDialog(false);
};
This behavior is only when I split it into child and parent.
In short, why does setOpenSnack(true);
in the child not work?
-
Snackbar
'sopen
prop expects boolean. You should usesetOpenSnack(true)
instead ofsetOpenSnack("true")
. – bertdida Commented Aug 8, 2020 at 2:20 - @bertdida Sure. I updated the demo. That does not fix the issue. Good catch though – Kal Commented Aug 8, 2020 at 2:23
- Basically you want to show the snackbar when the dialog is closed? – bertdida Commented Aug 8, 2020 at 2:24
- @bertdida I have simplified to show the issue but yes, the snackbar should not disappear when dialog is closed. They are separate elements. Seems like a bug but not sure. – Kal Commented Aug 8, 2020 at 2:25
-
Your
Snackbar
is sitting insideDialog
. When theDialog
unmounts/hidden, yourSnackbar
will also be unmounted. – bertdida Commented Aug 8, 2020 at 2:32
3 Answers
Reset to default 3Move your Snackbar
to your parent ponent so that it's not dependent on Dialog
's lifecycle.
App.js
import DialogBody from "./child";
function App() {
const [openDialog, setOpenDialog] = useState(false);
const [openSnack, setOpenSnack] = useState(false);
const [msg, setMsg] = useState("nothing");
function doApiCall(formData) {
// do your API calls here
console.log(formData);
// when done hide dialog, show snackbar
setOpenDialog(false);
setOpenSnack(true);
}
const fireOnClick = (event) => {
setMsg("you clicked a button");
setOpenDialog(true);
};
const handleCloseSnack = (event, reason) => {
if (reason === "clickaway") {
return;
}
setOpenSnack(false);
};
function Alert(props) {
return <MuiAlert elevation={6} variant="filled" {...props} />;
}
return (
<div className="App">
<h3>click button below to open dialog</h3>
<button onClick={() => fireOnClick()}>Click me</button>
<Dialog open={openDialog}>
<DialogTitle>This is a dialog</DialogTitle>
<DialogBody doApiCall={doApiCall} />
</Dialog>
<Snackbar
open={openSnack}
autoHideDuration={6000}
onClose={handleCloseSnack}
>
<Alert onClose={handleCloseSnack} severity="error">
{msg}
</Alert>
</Snackbar>
</div>
);
}
Child.js
export default function DialogBody({ doApiCall }) {
const [name, setName] = React.useState("");
function onChange(event) {
setName(event.target.value);
}
return (
<div>
<input type="text" value={name} onChange={onChange} />
<button onClick={() => doApiCall({ name })}>
Click this dialog button
</button>
</div>
);
}
Our DialogBody
ponent only accepts a prop called doApiCall
from our parent, which is invoked by clicking the Click this dialog button
.
doApiCall
might be an asynchronous call to your backends API which when resolves will hide the dialog and sets the snackbar to open.
When the dialog is closed, the Child ponent is dismantled. This justifies the disappearance of the Snack. Try this to see:
index.js
import React, { useState } from "react";
import ReactDOM from "react-dom";
import DialogTitle from "@material-ui/core/DialogTitle";
import Dialog from "@material-ui/core/Dialog";
import Child from "./child";
import Snackbar from "@material-ui/core/Snackbar";
import MuiAlert from "@material-ui/lab/Alert";
import "./styles.css";
function App() {
const [msg, setMsg] = useState("nothing");
const [openSnack, setOpenSnack] = useState(false);
const [openDialog, setOpenDialog] = useState(false);
//Close snackbar except when clickaway
const fireOnClick = (event) => {
setMsg("you clicked a button");
setOpenDialog(true);
//setOpenSnack(true);
};
const snackState = (value) => {
setOpenSnack(value)
}
const snackMessage = (value) => {
setMsg(value)
}
const handleCloseSnack = (event, reason) => {
if (reason === "clickaway") {
return;
}
setOpenSnack(false);
};
function Alert(props) {
return <MuiAlert elevation={6} variant="filled" {...props} />;
}
const closeDialogOnly = (event) => {
setMsg("you closed dialog only");
setOpenDialog(false);
};
return (
<div className="App">
<h3>click button below to open dialog</h3>
<button onClick={() => fireOnClick()}>Click me</button>
<Dialog onClose={closeDialogOnly} open={openDialog}>
<DialogTitle>This is a dialog</DialogTitle>
<Child snackbarStat={snackState} snackMessage={snackMessage} setOpenDialog={setOpenDialog} />
</Dialog>
<Snackbar
open={openSnack}
autoHideDuration={6000}
onClose={handleCloseSnack}
>
<Alert onClose={handleCloseSnack} severity="error">
{msg}
</Alert>
</Snackbar>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
child.js
import React from "react";
export default function Child({ snackbarStat, setOpenDialog, snackMessage }) {
const dialogClick = (event) => {
snackMessage("snackMessage")
snackbarStat(true);
setOpenDialog(false);
};
return (
<div className="child">
<button onClick={() => dialogClick()}>Click this dialog button</button>
<p>
{" "}
Clicking the button should close dialog but why is it closing the
snackbar also? <br />
The snack bar opens for one second and disappears.
<br />
setOpenSnack("true") lines is useless when dialog is false.
</p>
</div>
);
}
You have to place the Snackbar
in the parent ponent because when placing it in child ponent, it will disappear whenever the child is unmounted
Moreover, if you want to set the message, just pass the setMsg
to the child ponent and do the job
export default function Child({
openDialog,
setOpenDialog,
setMsg,
setOpenSnack
}) {
const dialogClick = (event) => {
setMsg("you closed from CHILD");
setOpenSnack(true);
setOpenDialog(false);
};
return (
<div className="child">
<button onClick={() => dialogClick()}>Click this dialog button</button>
<p>
...
</p>
</div>
);
}