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;