Skip to content

Commit

Permalink
[Snackbar] Fix timer restarting when parent component re-render (#18361)
Browse files Browse the repository at this point in the history
  • Loading branch information
Weslen do Nascimento authored and eps1lon committed Nov 18, 2019
1 parent 8e84f06 commit 44ded64
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 28 deletions.
43 changes: 16 additions & 27 deletions packages/material-ui/src/Snackbar/Snackbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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.
Expand All @@ -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]);

Expand Down
25 changes: 24 additions & 1 deletion packages/material-ui/src/Snackbar/Snackbar.test.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
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';

describe('<Snackbar />', () => {
let mount;
let classes;
const render = createClientRender({ strict: false });

before(() => {
classes = getClasses(<Snackbar open />);
Expand Down Expand Up @@ -130,6 +132,27 @@ describe('<Snackbar />', () => {
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(
<Snackbar
open={false}
onClose={handleClose1}
message="message"
autoHideDuration={autoHideDuration}
/>,
);

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;
Expand Down

0 comments on commit 44ded64

Please sign in to comment.