When the parent component re-renders before the autoHideDuration has elapsed, the Snackbar stays open. onClose is not called with the timeout reason.
If the Snackback autoHideDuration and onClose props don't change, it should call onClose with the timeout reason after autoHideDuration passes.
Steps:
https://codesandbox.io/s/hidden-waterfall-75ulu
( The Snackbar hides 2000ms after the timer is disabled: https://codesandbox.io/s/fervent-golick-fw1hg )
| Tech | Version |
| ----------- | ------- |
| Material-UI | v4.6.1 |
| React | v16.11.0|
@ambersz Thank you for the report. Right, it comes from the new reference of onClose you provide at each render, it resets the logic. We should be able to work around it, and simplify the logic with:
diff --git a/packages/material-ui/src/Snackbar/Snackbar.js b/packages/material-ui/src/Snackbar/Snackbar.js
index 5bf3e555e..b17def76a 100644
--- a/packages/material-ui/src/Snackbar/Snackbar.js
+++ b/packages/material-ui/src/Snackbar/Snackbar.js
@@ -4,6 +4,7 @@ import clsx from 'clsx';
import withStyles from '../styles/withStyles';
import { duration } from '../styles/transitions';
import ClickAwayListener from '../ClickAwayListener';
+import useEventCallback from '../utils/useEventCallback';
import capitalize from '../utils/capitalize';
import createChainedFunction from '../utils/createChainedFunction';
import Grow from '../Grow';
@@ -130,37 +131,26 @@ const Snackbar = React.forwardRef(function Snackbar(props, ref) {
const [exited, setExited] = React.useState(true);
// Timer that controls delay before snackbar auto hides
- const setAutoHideTimer = React.useCallback(
- autoHideDurationParam => {
- const autoHideDurationBefore =
- autoHideDurationParam != null ? autoHideDurationParam : autoHideDuration;
-
- if (!onClose || autoHideDurationBefore == null) {
- return;
- }
+ const setAutoHideTimer = useEventCallback(autoHideDurationParam => {
+ if (!onClose || autoHideDurationParam == null) {
+ if (!onClose || autoHideDurationParam == null) {
+ return;
+ }
- clearTimeout(timerAutoHide.current);
- timerAutoHide.current = setTimeout(() => {
- const autoHideDurationAfter =
- autoHideDurationParam != null ? autoHideDurationParam : autoHideDuration;
- if (!onClose || autoHideDurationAfter == null) {
- return;
- }
- onClose(null, 'timeout');
- }, autoHideDurationBefore);
- },
- [autoHideDuration, onClose],
- );
+ clearTimeout(timerAutoHide.current);
+ timerAutoHide.current = setTimeout(() => {
+ onClose(null, 'timeout');
+ }, autoHideDurationParam);
+ });
React.useEffect(() => {
if (open) {
- setAutoHideTimer();
+ setAutoHideTimer(autoHideDuration);
}
return () => {
clearTimeout(timerAutoHide.current);
};
- }, [open, setAutoHideTimer]);
+ }, [open, autoHideDuration, setAutoHideTimer]);
// Pause the timer when the user is interacting with the Snackbar
// or when the user hide the window.
What do you think? Do you want to submit a pull request? We could a test case at the same time.
I'm working on a PR for this problem :)