diff --git a/packages/material-ui/src/Snackbar/Snackbar.js b/packages/material-ui/src/Snackbar/Snackbar.js index 6693a2868ba3c4..ef3e08806e0eb6 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'; @@ -129,38 +130,30 @@ const Snackbar = React.forwardRef(function Snackbar(props, ref) { const timerAutoHide = React.useRef(); 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; + const handleClose = useEventCallback((...args) => { + onClose(...args); + }); - if (!onClose || autoHideDurationBefore == null) { - return; - } + const setAutoHideTimer = useEventCallback(autoHideDurationParam => { + if (!handleClose || 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(() => { + handleClose(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. @@ -172,11 +165,7 @@ const Snackbar = React.forwardRef(function Snackbar(props, ref) { // or when the window is shown back. const handleResume = React.useCallback(() => { if (autoHideDuration != null) { - if (resumeHideDuration != null) { - setAutoHideTimer(resumeHideDuration); - return; - } - setAutoHideTimer(autoHideDuration * 0.5); + setAutoHideTimer(resumeHideDuration != null ? resumeHideDuration : autoHideDuration * 0.5); } }, [autoHideDuration, resumeHideDuration, setAutoHideTimer]); diff --git a/packages/material-ui/src/Snackbar/Snackbar.test.js b/packages/material-ui/src/Snackbar/Snackbar.test.js index 1d84fe249b1237..a5c276015e79ed 100644 --- a/packages/material-ui/src/Snackbar/Snackbar.test.js +++ b/packages/material-ui/src/Snackbar/Snackbar.test.js @@ -1,7 +1,8 @@ import React from 'react'; -import { assert } from 'chai'; +import { expect, assert } from 'chai'; import { spy, useFakeTimers } from 'sinon'; import { createMount, getClasses } from '@material-ui/core/test-utils'; +import { createClientRender } from 'test/utils/createClientRender'; import describeConformance from '../test-utils/describeConformance'; import Snackbar from './Snackbar'; import Grow from '../Grow'; @@ -9,6 +10,7 @@ import Grow from '../Grow'; describe('', () => { let mount; let classes; + const render = createClientRender({ strict: false }); before(() => { classes = getClasses(); @@ -130,6 +132,27 @@ describe('', () => { assert.deepEqual(handleClose.args[0], [null, 'timeout']); }); + it('calls onClose at timeout even if the prop changes', () => { + const handleClose1 = spy(); + const handleClose2 = spy(); + const autoHideDuration = 2e3; + const { setProps } = render( + , + ); + + setProps({ open: true }); + clock.tick(autoHideDuration / 2); + setProps({ open: true, onClose: handleClose2 }); + clock.tick(autoHideDuration / 2); + expect(handleClose1.callCount).to.equal(0); + expect(handleClose2.callCount).to.equal(1); + }); + it('should not call onClose when the autoHideDuration is reset', () => { const handleClose = spy(); const autoHideDuration = 2e3;