batchedUpdates tests
Covers some bugs I encountered while working on this feature. Introduces a syncUpdates API to ReactNoop. Is this something we'd want to expose to the reconciler?
Andrew Clark committed
Oct 31, 2016 at 13:38 UTC
2182b690046a8794079fc3817223e98bf64072b8
4 files changed
+98
src/renderers/noop/ReactNoop.js
+4
@@ -257,6 +257,10 @@ var ReactNoop = {
257
NoopRenderer.performWithPriority(AnimationPriority, fn);
258
},
259
260
+ batchedUpdates: NoopRenderer.batchedUpdates,
261
+
262
+ syncUpdates: NoopRenderer.syncUpdates,
263
+
264
// Logs the current state of the tree.
265
dumpTree(rootID : string = DEFAULT_ROOT_ID) {
266
const root = roots.get(rootID);
src/renderers/shared/fiber/ReactFiberReconciler.js
+4
@@ -71,6 +71,7 @@ export type Reconciler<C, I, TI> = {
71
/* eslint-disable no-undef */
72
// FIXME: ESLint complains about type parameter
73
batchedUpdates<A>(fn : () => A) : A,
74
+ syncUpdates<A>(fn : () => A) : A,
75
/* eslint-enable no-undef */
76
77
// Used to extract the return value from the initial render. Legacy API.
@@ -86,6 +87,7 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) :
87
scheduleWork,
88
performWithPriority,
89
batchedUpdates,
90
+ syncUpdates,
91
} = ReactFiberScheduler(config);
92
93
return {
@@ -152,6 +154,8 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) :
154
155
batchedUpdates,
156
157
+ syncUpdates,
158
+
159
getPublicRootInstance(container : OpaqueNode) : (ReactComponent<any, any, any> | I | TI | null) {
160
const root : FiberRoot = (container.stateNode : any);
161
const containerFiber = root.current;
src/renderers/shared/fiber/ReactFiberScheduler.js
+11
@@ -712,10 +712,21 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
712
}
713
}
714
715
+ function syncUpdates<A>(fn : () => A) : A {
716
+ const previousPriorityContext = priorityContext;
717
+ priorityContext = SynchronousPriority;
718
+ try {
719
+ return fn();
720
+ } finally {
721
+ priorityContext = previousPriorityContext;
722
+ }
723
+ }
724
+
725
return {
726
scheduleWork: scheduleWork,
727
scheduleDeferredWork: scheduleDeferredWork,
728
performWithPriority: performWithPriority,
729
batchedUpdates: batchedUpdates,
730
+ syncUpdates: syncUpdates,
731
};
732
};
src/renderers/shared/fiber/__tests__/ReactIncremental-test.js
+79
@@ -1386,4 +1386,83 @@ describe('ReactIncremental', () => {
1386
]);
1387
});
1388
1389
+ it('performs batched updates at the end of the batch', () => {
1390
+ var ops = [];
1391
+ var instance;
1392
+
1393
+ class Foo extends React.Component {
1394
+ state = { n: 0 };
1395
+ render() {
1396
+ instance = this;
1397
+ return <div />;
1398
+ }
1399
+ }
1400
+
1401
+ ReactNoop.render(<Foo />);
1402
+ ReactNoop.flush();
1403
+ ops = [];
1404
+
1405
+ ReactNoop.syncUpdates(() => {
1406
+ ReactNoop.batchedUpdates(() => {
1407
+ instance.setState({ n: 1 }, () => ops.push('setState 1'));
1408
+ instance.setState({ n: 2 }, () => ops.push('setState 2'));
1409
+ ops.push('end batchedUpdates');
1410
+ });
1411
+ ops.push('end syncUpdates');
1412
+ });
1413
+
1414
+ // ReactNoop.flush() not needed because updates are synchronous
1415
+
1416
+ expect(ops).toEqual([
1417
+ 'end batchedUpdates',
1418
+ 'setState 1',
1419
+ 'setState 2',
1420
+ 'end syncUpdates',
1421
+ ]);
1422
+ expect(instance.state.n).toEqual(2);
1423
+ });
1424
+
1425
+ it('can nest batchedUpdates', () => {
1426
+ var ops = [];
1427
+ var instance;
1428
+
1429
+ class Foo extends React.Component {
1430
+ state = { n: 0 };
1431
+ render() {
1432
+ instance = this;
1433
+ return <div />;
1434
+ }
1435
+ }
1436
+
1437
+ ReactNoop.render(<Foo />);
1438
+ ReactNoop.flush();
1439
+ ops = [];
1440
+
1441
+ ReactNoop.syncUpdates(() => {
1442
+ ReactNoop.batchedUpdates(() => {
1443
+ instance.setState({ n: 1 }, () => ops.push('setState 1'));
1444
+ instance.setState({ n: 2 }, () => ops.push('setState 2'));
1445
+ ReactNoop.batchedUpdates(() => {
1446
+ instance.setState({ n: 3 }, () => ops.push('setState 3'));
1447
+ instance.setState({ n: 4 }, () => ops.push('setState 4'));
1448
+ ops.push('end inner batchedUpdates');
1449
+ });
1450
+ ops.push('end outer batchedUpdates');
1451
+ });
1452
+ ops.push('end syncUpdates');
1453
+ });
1454
+
1455
+ // ReactNoop.flush() not needed because updates are synchronous
1456
+
1457
+ expect(ops).toEqual([
1458
+ 'end inner batchedUpdates',
1459
+ 'end outer batchedUpdates',
1460
+ 'setState 1',
1461
+ 'setState 2',
1462
+ 'setState 3',
1463
+ 'setState 4',
1464
+ 'end syncUpdates',
1465
+ ]);
1466
+ expect(instance.state.n).toEqual(4);
1467
+ });
1468
});