Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Snackbar] Auto hide timer restarts when parent component re-renders #18353

Closed
2 tasks done
ambersz opened this issue Nov 13, 2019 · 2 comments · Fixed by #18361
Closed
2 tasks done

[Snackbar] Auto hide timer restarts when parent component re-renders #18353

ambersz opened this issue Nov 13, 2019 · 2 comments · Fixed by #18361
Labels
bug 🐛 Something doesn't work component: snackbar This is the name of the generic UI component, not the React module! good first issue Great for first contributions. Enable to learn the contribution process.

Comments

@ambersz
Copy link

ambersz commented Nov 13, 2019

  • The issue is present in the latest release.
  • I have searched the issues of this repository and believe that this is not a duplicate.

Current Behavior 😯

When the parent component re-renders before the autoHideDuration has elapsed, the Snackbar stays open. onClose is not called with the timeout reason.

Expected Behavior 🤔

If the Snackback autoHideDuration and onClose props don't change, it should call onClose with the timeout reason after autoHideDuration passes.

Steps to Reproduce 🕹

Steps:

  1. Create a component which displays a timer that counts up every 1000ms
  2. Within that component, create a Snackbar with autoHideDuration=2000ms

https://codesandbox.io/s/hidden-waterfall-75ulu

( The Snackbar hides 2000ms after the timer is disabled: https://codesandbox.io/s/fervent-golick-fw1hg )

Your Environment 🌎

Tech Version
Material-UI v4.6.1
React v16.11.0
@oliviertassinari oliviertassinari added component: snackbar This is the name of the generic UI component, not the React module! bug 🐛 Something doesn't work good first issue Great for first contributions. Enable to learn the contribution process. labels Nov 13, 2019
@oliviertassinari
Copy link
Member

@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.

@weslenng
Copy link

I'm working on a PR for this problem :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Something doesn't work component: snackbar This is the name of the generic UI component, not the React module! good first issue Great for first contributions. Enable to learn the contribution process.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants