最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - React Material UI - Snackbar closes when Dialog closes but its not supposed to - Stack Overflow

programmeradmin0浏览0评论

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?

Share Improve this question edited Aug 8, 2020 at 2:26 Kal asked Aug 8, 2020 at 1:54 KalKal 1,7744 gold badges30 silver badges48 bronze badges 6
  • Snackbar's open prop expects boolean. You should use setOpenSnack(true) instead of setOpenSnack("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 inside Dialog. When the Dialog unmounts/hidden, your Snackbar will also be unmounted. – bertdida Commented Aug 8, 2020 at 2:32
 |  Show 1 more ment

3 Answers 3

Reset to default 3

Move 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>
  );
}

发布评论

评论列表(0)

  1. 暂无评论