@samitouri / QOS-React-2 / commits / 0db4077c3a

Make React batching strategy injectable

Pete Hunt committed Aug 29, 2013 at 17:32 UTC 0db4077c3ac7c914c30169cce6ab7ddfb8aec3c9
3 files changed +144 -39
src/core/ReactDefaultBatchingStrategy.js new
+74
@@ -0,0 +1,74 @@
1 +/**
2 + * Copyright 2013 Facebook, Inc.
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + *
16 + * @providesModule ReactDefaultBatchingStrategy
17 + */
18 +
19 +"use strict";
20 +
21 +var ReactUpdates = require('ReactUpdates');
22 +var Transaction = require('Transaction');
23 +
24 +var emptyFunction = require('emptyFunction');
25 +var mixInto = require('mixInto');
26 +
27 +var RESET_BATCHED_UPDATES = {
28 + initialize: emptyFunction,
29 + close: function() {
30 + ReactDefaultBatchingStrategy.isBatchingUpdates = false;
31 + }
32 +};
33 +
34 +var FLUSH_BATCHED_UPDATES = {
35 + initialize: emptyFunction,
36 + close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
37 +};
38 +
39 +var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];
40 +
41 +function ReactDefaultBatchingStrategyTransaction() {
42 + this.reinitializeTransaction();
43 +}
44 +
45 +mixInto(ReactDefaultBatchingStrategyTransaction, Transaction.Mixin);
46 +mixInto(ReactDefaultBatchingStrategyTransaction, {
47 + getTransactionWrappers: function() {
48 + return TRANSACTION_WRAPPERS;
49 + }
50 +});
51 +
52 +var ReactDefaultBatchingStrategy = {
53 + isBatchingUpdates: false,
54 +
55 + /**
56 + * Call the provided function in a context within which calls to `setState`
57 + * and friends are batched such that components aren't updated unnecessarily.
58 + */
59 + batchedUpdates: function(callback) {
60 + var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
61 +
62 + ReactDefaultBatchingStrategy.isBatchingUpdates = true;
63 +
64 + // The code is written this way to avoid extra allocations
65 + if (alreadyBatchingUpdates) {
66 + callback();
67 + } else {
68 + var transaction = new ReactDefaultBatchingStrategyTransaction();
69 + transaction.perform(callback);
70 + }
71 + }
72 +};
73 +
74 +module.exports = ReactDefaultBatchingStrategy;
src/core/ReactDefaultInjection.js
+7
@@ -40,6 +40,9 @@ var ReactInstanceHandles = require('ReactInstanceHandles');
40 var SimpleEventPlugin = require('SimpleEventPlugin');
41 var MobileSafariClickEventPlugin = require('MobileSafariClickEventPlugin');
42
43 +var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
44 +var ReactUpdates = require('ReactUpdates');
45 +
46 function inject() {
47 ReactEventEmitter.TopLevelCallbackCreator = ReactEventTopLevelCallback;
48 /**
@@ -73,6 +76,10 @@ function inject() {
76 if (__DEV__) {
77 ReactPerf.injection.injectMeasure(require('ReactDefaultPerf').measure);
78 }
79 +
80 + ReactUpdates.injection.injectBatchingStrategy(
81 + ReactDefaultBatchingStrategy
82 + );
83 }
84
85 module.exports = {
src/core/ReactUpdates.js
+63 -39
@@ -20,51 +20,54 @@
20
21 var invariant = require('invariant');
22
23 -var isBatchingUpdates = false;
24 -
23 var dirtyComponents = [];
24
27 -/**
28 - * Call the provided function in a context within which calls to `setState` and
29 - * friends are batched such that components aren't updated unnecessarily.
30 - */
31 -function batchedUpdates(callback) {
32 - if (isBatchingUpdates) {
33 - // We're already executing in an environment where updates will be batched,
34 - // so this is a no-op.
35 - callback();
36 - return;
37 - }
25 +var batchingStrategy = null;
26
39 - isBatchingUpdates = true;
27 +function ensureBatchingStrategy() {
28 + invariant(batchingStrategy, 'ReactUpdates: must inject a batching strategy');
29 +}
30
41 - try {
42 - callback();
43 - // TODO: Sort components by depth such that parent components update first
44 - for (var i = 0; i < dirtyComponents.length; i++) {
45 - // If a component is unmounted before pending changes apply, ignore them
46 - // TODO: Queue unmounts in the same list to avoid this happening at all
47 - var component = dirtyComponents[i];
48 - if (component.isMounted()) {
49 - // If performUpdateIfNecessary happens to enqueue any new updates, we
50 - // shouldn't execute the callbacks until the next render happens, so
51 - // stash the callbacks first
52 - var callbacks = component._pendingCallbacks;
53 - component._pendingCallbacks = null;
54 - component.performUpdateIfNecessary();
55 - if (callbacks) {
56 - for (var j = 0; j < callbacks.length; j++) {
57 - callbacks[j].call(component);
58 - }
31 +function batchedUpdates(callback) {
32 + ensureBatchingStrategy();
33 + batchingStrategy.batchedUpdates(callback);
34 +}
35 +
36 +function runBatchedUpdates() {
37 + // TODO: Sort components by depth such that parent components update first
38 + for (var i = 0; i < dirtyComponents.length; i++) {
39 + // If a component is unmounted before pending changes apply, ignore them
40 + // TODO: Queue unmounts in the same list to avoid this happening at all
41 + var component = dirtyComponents[i];
42 + if (component.isMounted()) {
43 + // If performUpdateIfNecessary happens to enqueue any new updates, we
44 + // shouldn't execute the callbacks until the next render happens, so
45 + // stash the callbacks first
46 + var callbacks = component._pendingCallbacks;
47 + component._pendingCallbacks = null;
48 + component.performUpdateIfNecessary();
49 + if (callbacks) {
50 + for (var j = 0; j < callbacks.length; j++) {
51 + callbacks[j].call(component);
52 }
53 }
54 }
62 - } catch (error) {
63 - // IE8 requires `catch` in order to use `finally`.
64 - throw error;
55 + }
56 +}
57 +
58 +function clearDirtyComponents() {
59 + dirtyComponents.length = 0;
60 +}
61 +
62 +function flushBatchedUpdates() {
63 + // Run these in separate functions so the JIT can optimize
64 + try {
65 + runBatchedUpdates();
66 + } catch (e) {
67 + // IE 8 requires catch to use finally.
68 + throw e;
69 } finally {
66 - dirtyComponents.length = 0;
67 - isBatchingUpdates = false;
70 + clearDirtyComponents();
71 }
72 }
73
@@ -79,8 +82,9 @@ function enqueueUpdate(component, callback) {
82 '`setState`, `replaceState`, or `forceUpdate` with a callback that ' +
83 'isn\'t callable.'
84 );
85 + ensureBatchingStrategy();
86
83 - if (!isBatchingUpdates) {
87 + if (!batchingStrategy.isBatchingUpdates) {
88 component.performUpdateIfNecessary();
89 callback && callback();
90 return;
@@ -97,9 +101,29 @@ function enqueueUpdate(component, callback) {
101 }
102 }
103
104 +var ReactUpdatesInjection = {
105 + injectBatchingStrategy: function(_batchingStrategy) {
106 + invariant(
107 + _batchingStrategy,
108 + 'ReactUpdates: must provide a batching strategy'
109 + );
110 + invariant(
111 + typeof _batchingStrategy.batchedUpdates === 'function',
112 + 'ReactUpdates: must provide a batchedUpdates() function'
113 + );
114 + invariant(
115 + typeof _batchingStrategy.isBatchingUpdates === 'boolean',
116 + 'ReactUpdates: must provide an isBatchingUpdates boolean attribute'
117 + );
118 + batchingStrategy = _batchingStrategy;
119 + }
120 +};
121 +
122 var ReactUpdates = {
123 batchedUpdates: batchedUpdates,
102 - enqueueUpdate: enqueueUpdate
124 + enqueueUpdate: enqueueUpdate,
125 + flushBatchedUpdates: flushBatchedUpdates,
126 + injection: ReactUpdatesInjection
127 };
128
129 module.exports = ReactUpdates;