@samitouri / QOS-React / commits / 2182b69004

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 });