react / react-0.13.3 / examples / basic-commonjs / node_modules / reactify / node_modules / react-tools / src / core / ReactUpdates.js
81152 views/**1* Copyright 2013-2014, Facebook, Inc.2* All rights reserved.3*4* This source code is licensed under the BSD-style license found in the5* LICENSE file in the root directory of this source tree. An additional grant6* of patent rights can be found in the PATENTS file in the same directory.7*8* @providesModule ReactUpdates9*/1011"use strict";1213var CallbackQueue = require('CallbackQueue');14var PooledClass = require('PooledClass');15var ReactCurrentOwner = require('ReactCurrentOwner');16var ReactPerf = require('ReactPerf');17var Transaction = require('Transaction');1819var assign = require('Object.assign');20var invariant = require('invariant');21var warning = require('warning');2223var dirtyComponents = [];24var asapCallbackQueue = CallbackQueue.getPooled();25var asapEnqueued = false;2627var batchingStrategy = null;2829function ensureInjected() {30invariant(31ReactUpdates.ReactReconcileTransaction && batchingStrategy,32'ReactUpdates: must inject a reconcile transaction class and batching ' +33'strategy'34);35}3637var NESTED_UPDATES = {38initialize: function() {39this.dirtyComponentsLength = dirtyComponents.length;40},41close: function() {42if (this.dirtyComponentsLength !== dirtyComponents.length) {43// Additional updates were enqueued by componentDidUpdate handlers or44// similar; before our own UPDATE_QUEUEING wrapper closes, we want to run45// these new updates so that if A's componentDidUpdate calls setState on46// B, B will update before the callback A's updater provided when calling47// setState.48dirtyComponents.splice(0, this.dirtyComponentsLength);49flushBatchedUpdates();50} else {51dirtyComponents.length = 0;52}53}54};5556var UPDATE_QUEUEING = {57initialize: function() {58this.callbackQueue.reset();59},60close: function() {61this.callbackQueue.notifyAll();62}63};6465var TRANSACTION_WRAPPERS = [NESTED_UPDATES, UPDATE_QUEUEING];6667function ReactUpdatesFlushTransaction() {68this.reinitializeTransaction();69this.dirtyComponentsLength = null;70this.callbackQueue = CallbackQueue.getPooled();71this.reconcileTransaction =72ReactUpdates.ReactReconcileTransaction.getPooled();73}7475assign(76ReactUpdatesFlushTransaction.prototype,77Transaction.Mixin, {78getTransactionWrappers: function() {79return TRANSACTION_WRAPPERS;80},8182destructor: function() {83this.dirtyComponentsLength = null;84CallbackQueue.release(this.callbackQueue);85this.callbackQueue = null;86ReactUpdates.ReactReconcileTransaction.release(this.reconcileTransaction);87this.reconcileTransaction = null;88},8990perform: function(method, scope, a) {91// Essentially calls `this.reconcileTransaction.perform(method, scope, a)`92// with this transaction's wrappers around it.93return Transaction.Mixin.perform.call(94this,95this.reconcileTransaction.perform,96this.reconcileTransaction,97method,98scope,99a100);101}102});103104PooledClass.addPoolingTo(ReactUpdatesFlushTransaction);105106function batchedUpdates(callback, a, b) {107ensureInjected();108batchingStrategy.batchedUpdates(callback, a, b);109}110111/**112* Array comparator for ReactComponents by owner depth113*114* @param {ReactComponent} c1 first component you're comparing115* @param {ReactComponent} c2 second component you're comparing116* @return {number} Return value usable by Array.prototype.sort().117*/118function mountDepthComparator(c1, c2) {119return c1._mountDepth - c2._mountDepth;120}121122function runBatchedUpdates(transaction) {123var len = transaction.dirtyComponentsLength;124invariant(125len === dirtyComponents.length,126'Expected flush transaction\'s stored dirty-components length (%s) to ' +127'match dirty-components array length (%s).',128len,129dirtyComponents.length130);131132// Since reconciling a component higher in the owner hierarchy usually (not133// always -- see shouldComponentUpdate()) will reconcile children, reconcile134// them before their children by sorting the array.135dirtyComponents.sort(mountDepthComparator);136137for (var i = 0; i < len; i++) {138// If a component is unmounted before pending changes apply, ignore them139// TODO: Queue unmounts in the same list to avoid this happening at all140var component = dirtyComponents[i];141if (component.isMounted()) {142// If performUpdateIfNecessary happens to enqueue any new updates, we143// shouldn't execute the callbacks until the next render happens, so144// stash the callbacks first145var callbacks = component._pendingCallbacks;146component._pendingCallbacks = null;147component.performUpdateIfNecessary(transaction.reconcileTransaction);148149if (callbacks) {150for (var j = 0; j < callbacks.length; j++) {151transaction.callbackQueue.enqueue(152callbacks[j],153component154);155}156}157}158}159}160161var flushBatchedUpdates = ReactPerf.measure(162'ReactUpdates',163'flushBatchedUpdates',164function() {165// ReactUpdatesFlushTransaction's wrappers will clear the dirtyComponents166// array and perform any updates enqueued by mount-ready handlers (i.e.,167// componentDidUpdate) but we need to check here too in order to catch168// updates enqueued by setState callbacks and asap calls.169while (dirtyComponents.length || asapEnqueued) {170if (dirtyComponents.length) {171var transaction = ReactUpdatesFlushTransaction.getPooled();172transaction.perform(runBatchedUpdates, null, transaction);173ReactUpdatesFlushTransaction.release(transaction);174}175176if (asapEnqueued) {177asapEnqueued = false;178var queue = asapCallbackQueue;179asapCallbackQueue = CallbackQueue.getPooled();180queue.notifyAll();181CallbackQueue.release(queue);182}183}184}185);186187/**188* Mark a component as needing a rerender, adding an optional callback to a189* list of functions which will be executed once the rerender occurs.190*/191function enqueueUpdate(component, callback) {192invariant(193!callback || typeof callback === "function",194'enqueueUpdate(...): You called `setProps`, `replaceProps`, ' +195'`setState`, `replaceState`, or `forceUpdate` with a callback that ' +196'isn\'t callable.'197);198ensureInjected();199200// Various parts of our code (such as ReactCompositeComponent's201// _renderValidatedComponent) assume that calls to render aren't nested;202// verify that that's the case. (This is called by each top-level update203// function, like setProps, setState, forceUpdate, etc.; creation and204// destruction of top-level components is guarded in ReactMount.)205warning(206ReactCurrentOwner.current == null,207'enqueueUpdate(): Render methods should be a pure function of props ' +208'and state; triggering nested component updates from render is not ' +209'allowed. If necessary, trigger nested updates in ' +210'componentDidUpdate.'211);212213if (!batchingStrategy.isBatchingUpdates) {214batchingStrategy.batchedUpdates(enqueueUpdate, component, callback);215return;216}217218dirtyComponents.push(component);219220if (callback) {221if (component._pendingCallbacks) {222component._pendingCallbacks.push(callback);223} else {224component._pendingCallbacks = [callback];225}226}227}228229/**230* Enqueue a callback to be run at the end of the current batching cycle. Throws231* if no updates are currently being performed.232*/233function asap(callback, context) {234invariant(235batchingStrategy.isBatchingUpdates,236'ReactUpdates.asap: Can\'t enqueue an asap callback in a context where' +237'updates are not being batched.'238);239asapCallbackQueue.enqueue(callback, context);240asapEnqueued = true;241}242243var ReactUpdatesInjection = {244injectReconcileTransaction: function(ReconcileTransaction) {245invariant(246ReconcileTransaction,247'ReactUpdates: must provide a reconcile transaction class'248);249ReactUpdates.ReactReconcileTransaction = ReconcileTransaction;250},251252injectBatchingStrategy: function(_batchingStrategy) {253invariant(254_batchingStrategy,255'ReactUpdates: must provide a batching strategy'256);257invariant(258typeof _batchingStrategy.batchedUpdates === 'function',259'ReactUpdates: must provide a batchedUpdates() function'260);261invariant(262typeof _batchingStrategy.isBatchingUpdates === 'boolean',263'ReactUpdates: must provide an isBatchingUpdates boolean attribute'264);265batchingStrategy = _batchingStrategy;266}267};268269var ReactUpdates = {270/**271* React references `ReactReconcileTransaction` using this property in order272* to allow dependency injection.273*274* @internal275*/276ReactReconcileTransaction: null,277278batchedUpdates: batchedUpdates,279enqueueUpdate: enqueueUpdate,280flushBatchedUpdates: flushBatchedUpdates,281injection: ReactUpdatesInjection,282asap: asap283};284285module.exports = ReactUpdates;286287288