/** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule TimerMixin * @flow */ 'use strict'; var setImmediate = require('setImmediate'); var clearImmediate = require('clearImmediate'); /** * Using bare setTimeout, setInterval, setImmediate and * requestAnimationFrame calls is very dangerous because if you forget to cancel * the request before the component is unmounted, you risk the callback throwing * an exception. * * If you include TimerMixin, then you can replace your calls * to `setTimeout(fn, 500)` * with `this.setTimeout(fn, 500)` (just prepend `this.`) * and everything will be properly cleaned up for you. * * Example: * * var Component = React.createClass({ * mixins: [TimerMixin], * componentDidMount: function() { * this.setTimeout( * () => { console.log('I do not leak!'); }, * 500 * ); * } * }); */ var setter = function(setter, clearer, array) { return function( callback: () => void, delta: number ): number { var id = setter(() => { clearer.call(this, id); callback.apply(this, arguments); }, delta); if (!this[array]) { this[array] = [id]; } else { this[array].push(id); } return id; }; }; var clearer = function(clearer, array) { return function(id: number) { if (this[array]) { var index = this[array].indexOf(id); if (index !== -1) { this[array].splice(index, 1); } } clearer(id); }; }; var _timeouts = 'TimerMixin_timeouts'; var _clearTimeout = clearer(clearTimeout, _timeouts); var _setTimeout = setter(setTimeout, _clearTimeout, _timeouts); var _intervals = 'TimerMixin_intervals'; var _clearInterval = clearer(clearInterval, _intervals); var _setInterval = setter(setInterval, () => {/* noop */}, _intervals); var _immediates = 'TimerMixin_immediates'; var _clearImmediate = clearer(clearImmediate, _immediates); var _setImmediate = setter(setImmediate, _clearImmediate, _immediates); var _rafs = 'TimerMixin_rafs'; var _cancelAnimationFrame = clearer(window.cancelAnimationFrame, _rafs); var _requestAnimationFrame = setter(window.requestAnimationFrame, _cancelAnimationFrame, _rafs); var TimerMixin = { componentWillUnmount: function() { this[_timeouts] && this[_timeouts].forEach(this.clearTimeout); this[_intervals] && this[_intervals].forEach(this.clearInterval); this[_immediates] && this[_immediates].forEach(this.clearImmediate); this[_rafs] && this[_rafs].forEach(this.cancelAnimationFrame); }, setTimeout: _setTimeout, clearTimeout: _clearTimeout, setInterval: _setInterval, clearInterval: _clearInterval, setImmediate: _setImmediate, clearImmediate: _clearImmediate, requestAnimationFrame: _requestAnimationFrame, cancelAnimationFrame: _cancelAnimationFrame, }; module.exports = TimerMixin;