[Fiber] Fix to call deferred callbackQueue even if updates are aborted (#9634)
* Fix to call deferred callbackQueue even if updates are aborted * Add an assertion to make sure the update processed, but wasn't committed yet
Toru Kobayashi committed
May 13, 2017 at 02:45 UTC
17ab69c1ec9e2d994bb58955214c463df3b377cb
3 files changed
+57
-1
scripts/fiber/tests-passing.txt
+1
@@ -1673,6 +1673,7 @@ src/renderers/shared/fiber/__tests__/ReactIncremental-test.js
1673
* should render a simple component, in steps if needed
1674
* updates a previous render
1675
* can cancel partially rendered work and restart
1676
+* should call callbacks even if updates are aborted
1677
* can deprioritize unfinished work and resume it later
1678
* can deprioritize a tree from without dropping work
1679
* can resume work in a subtree even when a parent bails out
src/renderers/shared/fiber/ReactFiberUpdateQueue.js
+1
-1
@@ -426,7 +426,7 @@ function beginUpdateQueue(
426
// a new state object.
427
let state = prevState;
428
let dontMutatePrevState = true;
429
- let callbackList = null;
429
+ let callbackList = queue.callbackList;
430
let update = queue.first;
431
while (
432
update !== null &&
src/renderers/shared/fiber/__tests__/ReactIncremental-test.js
+55
@@ -183,6 +183,61 @@ describe('ReactIncremental', () => {
183
expect(ops).toEqual(['Foo', 'Bar', 'Bar']);
184
});
185
186
+ it('should call callbacks even if updates are aborted', () => {
187
+ const ops = [];
188
+ let inst;
189
+
190
+ class Foo extends React.Component {
191
+ constructor(props) {
192
+ super(props);
193
+ this.state = {
194
+ text: 'foo',
195
+ text2: 'foo',
196
+ };
197
+ inst = this;
198
+ }
199
+ render() {
200
+ return (
201
+ <div>
202
+ <div>{this.state.text}</div>
203
+ <div>{this.state.text2}</div>
204
+ </div>
205
+ );
206
+ }
207
+ }
208
+
209
+ ReactNoop.render(<Foo />);
210
+ ReactNoop.flush();
211
+
212
+ inst.setState(
213
+ () => {
214
+ ops.push('setState1');
215
+ return {text: 'bar'};
216
+ },
217
+ () => ops.push('callback1'),
218
+ );
219
+
220
+ // Flush part of the work
221
+ ReactNoop.flushDeferredPri(20 + 5);
222
+
223
+ expect(ops).toEqual(['setState1']);
224
+
225
+ // This will abort the previous work and restart
226
+ inst.setState(
227
+ () => {
228
+ ops.push('setState2');
229
+ return {text2: 'baz'};
230
+ },
231
+ () => ops.push('callback2'),
232
+ );
233
+
234
+ // Flush the rest of the work which now includes the low priority
235
+ ReactNoop.flush();
236
+
237
+ expect(ops).toEqual(['setState1', 'setState2', 'callback1', 'callback2']);
238
+ expect(inst.state).toEqual({text: 'bar', text2: 'baz'});
239
+ });
240
+
241
it('can deprioritize unfinished work and resume it later', () => {
242
var ops = [];
243