18
18
19
19
"use strict" ;
20
20
21
+ var CallbackQueue = require ( 'CallbackQueue' ) ;
22
+ var PooledClass = require ( 'PooledClass' ) ;
21
23
var ReactCurrentOwner = require ( 'ReactCurrentOwner' ) ;
22
24
var ReactPerf = require ( 'ReactPerf' ) ;
25
+ var Transaction = require ( 'Transaction' ) ;
23
26
24
27
var invariant = require ( 'invariant' ) ;
28
+ var mixInto = require ( 'mixInto' ) ;
25
29
var warning = require ( 'warning' ) ;
26
30
27
31
var dirtyComponents = [ ] ;
@@ -36,9 +40,77 @@ function ensureInjected() {
36
40
) ;
37
41
}
38
42
39
- function batchedUpdates ( callback , param ) {
43
+ var NESTED_UPDATES = {
44
+ initialize : function ( ) {
45
+ this . dirtyComponentsLength = dirtyComponents . length ;
46
+ } ,
47
+ close : function ( ) {
48
+ if ( this . dirtyComponentsLength !== dirtyComponents . length ) {
49
+ // Additional updates were enqueued by componentDidUpdate handlers or
50
+ // similar; before our own UPDATE_QUEUEING wrapper closes, we want to run
51
+ // these new updates so that if A's componentDidUpdate calls setState on
52
+ // B, B will update before the callback A's updater provided when calling
53
+ // setState.
54
+ dirtyComponents . splice ( 0 , this . dirtyComponentsLength ) ;
55
+ flushBatchedUpdates ( ) ;
56
+ } else {
57
+ dirtyComponents . length = 0 ;
58
+ }
59
+ }
60
+ } ;
61
+
62
+ var UPDATE_QUEUEING = {
63
+ initialize : function ( ) {
64
+ this . callbackQueue . reset ( ) ;
65
+ } ,
66
+ close : function ( ) {
67
+ this . callbackQueue . notifyAll ( ) ;
68
+ }
69
+ } ;
70
+
71
+ var TRANSACTION_WRAPPERS = [ NESTED_UPDATES , UPDATE_QUEUEING ] ;
72
+
73
+ function ReactUpdatesFlushTransaction ( ) {
74
+ this . reinitializeTransaction ( ) ;
75
+ this . dirtyComponentsLength = null ;
76
+ this . callbackQueue = CallbackQueue . getPooled ( null ) ;
77
+ this . reconcileTransaction =
78
+ ReactUpdates . ReactReconcileTransaction . getPooled ( ) ;
79
+ }
80
+
81
+ mixInto ( ReactUpdatesFlushTransaction , Transaction . Mixin ) ;
82
+ mixInto ( ReactUpdatesFlushTransaction , {
83
+ getTransactionWrappers : function ( ) {
84
+ return TRANSACTION_WRAPPERS ;
85
+ } ,
86
+
87
+ destructor : function ( ) {
88
+ this . dirtyComponentsLength = null ;
89
+ CallbackQueue . release ( this . callbackQueue ) ;
90
+ this . callbackQueue = null ;
91
+ ReactUpdates . ReactReconcileTransaction . release ( this . reconcileTransaction ) ;
92
+ this . reconcileTransaction = null ;
93
+ } ,
94
+
95
+ perform : function ( method , scope , a ) {
96
+ // Essentially calls `this.reconcileTransaction.perform(method, scope, a)`
97
+ // with this transaction's wrappers around it.
98
+ return Transaction . Mixin . perform . call (
99
+ this ,
100
+ this . reconcileTransaction . perform ,
101
+ this . reconcileTransaction ,
102
+ method ,
103
+ scope ,
104
+ a
105
+ ) ;
106
+ }
107
+ } ) ;
108
+
109
+ PooledClass . addPoolingTo ( ReactUpdatesFlushTransaction ) ;
110
+
111
+ function batchedUpdates ( callback , a , b ) {
40
112
ensureInjected ( ) ;
41
- batchingStrategy . batchedUpdates ( callback , param ) ;
113
+ batchingStrategy . batchedUpdates ( callback , a , b ) ;
42
114
}
43
115
44
116
/**
@@ -53,13 +125,21 @@ function mountDepthComparator(c1, c2) {
53
125
}
54
126
55
127
function runBatchedUpdates ( transaction ) {
128
+ var len = transaction . dirtyComponentsLength ;
129
+ invariant (
130
+ len === dirtyComponents . length ,
131
+ 'Expected flush transaction\'s stored dirty-components length (%s) to ' +
132
+ 'match dirty-components array length (%s).' ,
133
+ len ,
134
+ dirtyComponents . length
135
+ ) ;
136
+
56
137
// Since reconciling a component higher in the owner hierarchy usually (not
57
138
// always -- see shouldComponentUpdate()) will reconcile children, reconcile
58
139
// them before their children by sorting the array.
59
-
60
140
dirtyComponents . sort ( mountDepthComparator ) ;
61
141
62
- for ( var i = 0 ; i < dirtyComponents . length ; i ++ ) {
142
+ for ( var i = 0 ; i < len ; i ++ ) {
63
143
// If a component is unmounted before pending changes apply, ignore them
64
144
// TODO: Queue unmounts in the same list to avoid this happening at all
65
145
var component = dirtyComponents [ i ] ;
@@ -69,11 +149,11 @@ function runBatchedUpdates(transaction) {
69
149
// stash the callbacks first
70
150
var callbacks = component . _pendingCallbacks ;
71
151
component . _pendingCallbacks = null ;
72
- component . performUpdateIfNecessary ( transaction ) ;
152
+ component . performUpdateIfNecessary ( transaction . reconcileTransaction ) ;
73
153
74
154
if ( callbacks ) {
75
155
for ( var j = 0 ; j < callbacks . length ; j ++ ) {
76
- transaction . getReactMountReady ( ) . enqueue (
156
+ transaction . callbackQueue . enqueue (
77
157
callbacks [ j ] ,
78
158
component
79
159
) ;
@@ -83,30 +163,18 @@ function runBatchedUpdates(transaction) {
83
163
}
84
164
}
85
165
86
- function clearDirtyComponents ( ) {
87
- dirtyComponents . length = 0 ;
88
- }
89
-
90
- function flushBatchedUpdatesOnce ( transaction ) {
91
- // Run these in separate functions so the JIT can optimize
92
- try {
93
- runBatchedUpdates ( transaction ) ;
94
- } finally {
95
- clearDirtyComponents ( ) ;
96
- }
97
- }
98
-
99
166
var flushBatchedUpdates = ReactPerf . measure (
100
167
'ReactUpdates' ,
101
168
'flushBatchedUpdates' ,
102
169
function ( ) {
103
- // flushBatchedUpdatesOnce will clear the dirtyComponents array, but
104
- // mount-ready handlers (i.e., componentDidMount/Update) may enqueue more
105
- // state updates which we should apply immediately
170
+ // ReactUpdatesFlushTransaction's wrappers will clear the dirtyComponents
171
+ // array and perform any updates enqueued by mount-ready handlers (i.e.,
172
+ // componentDidUpdate) but we need to check here too in order to catch
173
+ // updates enqueued by setState callbacks.
106
174
while ( dirtyComponents . length ) {
107
- var transaction = ReactUpdates . ReactReconcileTransaction . getPooled ( ) ;
108
- transaction . perform ( flushBatchedUpdatesOnce , null , transaction ) ;
109
- ReactUpdates . ReactReconcileTransaction . release ( transaction ) ;
175
+ var transaction = ReactUpdatesFlushTransaction . getPooled ( ) ;
176
+ transaction . perform ( runBatchedUpdates , null , transaction ) ;
177
+ ReactUpdatesFlushTransaction . release ( transaction ) ;
110
178
}
111
179
}
112
180
) ;
@@ -138,14 +206,7 @@ function enqueueUpdate(component, callback) {
138
206
) ;
139
207
140
208
if ( ! batchingStrategy . isBatchingUpdates ) {
141
- var transaction = ReactUpdates . ReactReconcileTransaction . getPooled ( ) ;
142
- transaction . perform (
143
- component . performUpdateIfNecessary ,
144
- component ,
145
- transaction
146
- ) ;
147
- ReactUpdates . ReactReconcileTransaction . release ( transaction ) ;
148
- callback && callback . call ( component ) ;
209
+ batchingStrategy . batchedUpdates ( enqueueUpdate , component , callback ) ;
149
210
return ;
150
211
}
151
212
0 commit comments