diff --git a/packages/material-ui-lab/src/Slider/Slider.js b/packages/material-ui-lab/src/Slider/Slider.js index 77a2685d8dfea9..c7ce8ce08e94d9 100644 --- a/packages/material-ui-lab/src/Slider/Slider.js +++ b/packages/material-ui-lab/src/Slider/Slider.js @@ -134,15 +134,6 @@ export const styles = theme => { }; }; -function addEventListener(node, event, handler, capture) { - node.addEventListener(event, handler, capture); - return { - remove: function remove() { - node.removeEventListener(event, handler, capture); - }, - }; -} - function percentToValue(percent, min, max) { return ((max - min) * percent) / 100 + min; } @@ -198,6 +189,8 @@ if (process.env.NODE_ENV !== 'production' && !React.createContext) { class Slider extends React.Component { state = { currentState: 'initial' }; + jumpAnimationTimeoutId = -1; + componentDidMount() { if (this.containerRef) { this.containerRef.addEventListener('touchstart', preventPageScrolling, { passive: false }); @@ -206,6 +199,9 @@ class Slider extends React.Component { componentWillUnmount() { this.containerRef.removeEventListener('touchstart', preventPageScrolling, { passive: false }); + document.body.removeEventListener('mousemove', this.handleMouseMove); + document.body.removeEventListener('touchend', this.handleMouseUp); + clearTimeout(this.jumpAnimationTimeoutId); } static getDerivedStateFromProps(nextProps, prevState) { @@ -281,7 +277,7 @@ class Slider extends React.Component { event.preventDefault(); this.setState({ currentState: 'activated' }); - this.globalMouseUpListener = addEventListener(document, 'touchend', this.handleMouseUp); + document.body.addEventListener('touchend', this.handleMouseUp); if (typeof this.props.onDragStart === 'function') { this.props.onDragStart(event); @@ -292,8 +288,8 @@ class Slider extends React.Component { event.preventDefault(); this.setState({ currentState: 'activated' }); - this.globalMouseUpListener = addEventListener(document, 'mouseup', this.handleMouseUp); - this.globalMouseMoveListener = addEventListener(document, 'mousemove', this.handleMouseMove); + document.body.addEventListener('touchend', this.handleMouseUp); + document.body.addEventListener('mousemove', this.handleMouseMove); if (typeof this.props.onDragStart === 'function') { this.props.onDragStart(event); @@ -303,13 +299,8 @@ class Slider extends React.Component { handleMouseUp = event => { this.setState({ currentState: 'normal' }); - if (this.globalMouseUpListener) { - this.globalMouseUpListener.remove(); - } - - if (this.globalMouseMoveListener) { - this.globalMouseMoveListener.remove(); - } + document.body.removeEventListener('mousemove', this.handleMouseMove); + document.body.removeEventListener('touchend', this.handleMouseUp); if (typeof this.props.onDragEnd === 'function') { this.props.onDragEnd(event); @@ -373,7 +364,8 @@ class Slider extends React.Component { playJumpAnimation() { this.setState({ currentState: 'jumped' }, () => { - setTimeout(() => { + clearTimeout(this.jumpAnimationTimeoutId); + this.jumpAnimationTimeoutId = setTimeout(() => { this.setState({ currentState: 'normal' }); }, this.props.theme.transitions.duration.complex); }); diff --git a/packages/material-ui-lab/src/Slider/Slider.test.js b/packages/material-ui-lab/src/Slider/Slider.test.js index a9be9f44a3932f..788bf375605fff 100644 --- a/packages/material-ui-lab/src/Slider/Slider.test.js +++ b/packages/material-ui-lab/src/Slider/Slider.test.js @@ -55,6 +55,24 @@ describe('', () => { assert.strictEqual(handleDragEnd.callCount, 1, 'should have called the handleDragEnd cb'); }); + it('should not cause leaks', () => { + const wrapper = mount(); + + wrapper.simulate('mousedown'); + wrapper.unmount(); + + // would cause errors because we try to access refs that are null + const mousemoveEvent = document.createEvent('HTMLEvents'); + mousemoveEvent.initEvent('mousemove', false, true); + document.dispatchEvent(mousemoveEvent); + + // if we did not remove the global event listener react will throw a warning + // because we called setState on an unmounted component + const mouseupEvent = document.createEvent('HTMLEvents'); + mouseupEvent.initEvent('mouseup', false, true); + document.dispatchEvent(mouseupEvent); + }); + describe('prop: vertical', () => { it('should render with the default and vertical classes', () => { const wrapper = shallow();