-
-
Save STRML/cc368c46c2d7de8679196e69ddbcaef7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @flow | |
import ReactUpdates from 'react-dom/lib/ReactUpdates'; | |
// visibility unfortunately touches 'document' so we have to hide it here | |
const visibilityWatcher = process.browser ? require('visibility')() : {}; | |
const FORCE_TICK_INTERVAL = 1000; | |
let tickForced = false; | |
let callbacks = []; | |
const ReactRAFBatchingStrategy = { | |
// Every time React does a transaction, it checks this var. | |
// If it's false, it's going to go ahead and call `batchedUpdates`. | |
// Otherwise, it appends to `dirtyComponents` and waits for a flush. | |
// | |
// When this is false, we get default React behavior. | |
// | |
// When this is true, we'll start collecting components to render and wait for a flush. | |
// This is good to do when WS data comes down. | |
isBatchingUpdates: false, | |
/** | |
* Call the provided function in a context within which calls to `setState` | |
* and friends are batched such that components aren't updated unnecessarily. | |
*/ | |
batchedUpdates(callback: Function, a: any, b: any, c: any, d: any, e: any) { | |
const wasBatching = this.isBatchingUpdates; | |
// If we were batching already, it's safe to call back on this. That will enqueue the update. | |
if (wasBatching) { | |
return callback(a, b, c, d, e); | |
} | |
// If we weren't batching, do what the default strategy does: say we're batching, enqueue the update, | |
// then flush it. | |
// Note that you *must* set `isBatchingUpdates` to true before calling back, otherwise the callback | |
// will call batchedUpdates again and you'll blow the stack. | |
// See ReactDefaultBatchingStrategy.js. It's complex to read b/c it uses a Transaction, | |
// but see the 'close' handlers of each transaction wrapper. Those are executed after the callback. | |
this.isBatchingUpdates = true; | |
callback(a, b, c, d, e); // eslint-disable-line callback-return | |
flushCallbacks(); | |
ReactUpdates.flushBatchedUpdates(); | |
this.isBatchingUpdates = false; | |
}, | |
// Call this function on next tick. | |
onNextTick(fn: Function) { | |
callbacks.push(fn); | |
}, | |
batch() { | |
// If we're not already batching, flush on the next rAF. | |
const wasBatching = this.isBatchingUpdates; | |
if (!wasBatching) { | |
this.isBatchingUpdates = true; | |
// If the page is hidden, rAF will never fire. This is generally good but we have things like native | |
// order fill notifications that users should see. | |
if (visibilityWatcher && visibilityWatcher.hidden()) { | |
tickForced = setTimeout(tick, FORCE_TICK_INTERVAL); | |
} else { | |
window.requestAnimationFrame(tick); | |
} | |
} | |
}, | |
inject() { | |
// If the page becomes visible, flush updates right away if they were waiting for the force interval. | |
visibilityWatcher && visibilityWatcher.on('show', () => { | |
if (!tickForced) return; | |
clearTimeout(tickForced); | |
tick(); | |
}); | |
// If the page is hidden, start the forced tick interval. Otherwise it's possible | |
// for us to have been waiting on rAF and this just never goes. | |
visibilityWatcher && visibilityWatcher.on('hide', () => { | |
if (tickForced) return; | |
tickForced = setTimeout(tick, FORCE_TICK_INTERVAL); | |
}); | |
ReactUpdates.injection.injectBatchingStrategy(this); | |
} | |
}; | |
// Only flush if this has been explicitly enabled. | |
// This means we're going to enable it every time we get a WS message, but otherwise it doesn't tick. | |
function tick() { | |
tickForced = null; | |
flushCallbacks(); | |
ReactUpdates.flushBatchedUpdates(); | |
ReactRAFBatchingStrategy.isBatchingUpdates = false; | |
} | |
function flushCallbacks() { | |
for (let i = 0; i < callbacks.length; i++) { | |
callbacks[i](); | |
} | |
callbacks = []; | |
} | |
export default ReactRAFBatchingStrategy; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment