Skip to content

Commit f52e512

Browse files
committed
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?
1 parent cf8fe73 commit f52e512

File tree

4 files changed

+98
-0
lines changed

4 files changed

+98
-0
lines changed

src/renderers/noop/ReactNoop.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,10 @@ var ReactNoop = {
237237
NoopRenderer.performWithPriority(AnimationPriority, fn);
238238
},
239239

240+
batchedUpdates: NoopRenderer.batchedUpdates,
241+
242+
syncUpdates: NoopRenderer.syncUpdates,
243+
240244
// Logs the current state of the tree.
241245
dumpTree(rootID : string = DEFAULT_ROOT_ID) {
242246
const root = roots.get(rootID);

src/renderers/shared/fiber/ReactFiberReconciler.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export type Reconciler<C, I, TI> = {
7171
/* eslint-disable no-undef */
7272
// FIXME: ESLint complains about type parameter
7373
batchedUpdates<A>(fn : () => A) : A,
74+
syncUpdates<A>(fn : () => A) : A,
7475
/* eslint-enable no-undef */
7576

7677
// 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>) :
8687
scheduleWork,
8788
performWithPriority,
8889
batchedUpdates,
90+
syncUpdates,
8991
} = ReactFiberScheduler(config);
9092

9193
return {
@@ -152,6 +154,8 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) :
152154

153155
batchedUpdates,
154156

157+
syncUpdates,
158+
155159
getPublicRootInstance(container : OpaqueNode) : (ReactComponent<any, any, any> | I | TI | null) {
156160
const root : FiberRoot = (container.stateNode : any);
157161
const containerFiber = root.current;

src/renderers/shared/fiber/ReactFiberScheduler.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,10 +712,21 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
712712
}
713713
}
714714

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+
715725
return {
716726
scheduleWork: scheduleWork,
717727
scheduleDeferredWork: scheduleDeferredWork,
718728
performWithPriority: performWithPriority,
719729
batchedUpdates: batchedUpdates,
730+
syncUpdates: syncUpdates,
720731
};
721732
};

src/renderers/shared/fiber/__tests__/ReactIncremental-test.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1385,4 +1385,83 @@ describe('ReactIncremental', () => {
13851385
]);
13861386
});
13871387

1388+
it('performs batched updates at the end of the batch', () => {
1389+
var ops = [];
1390+
var instance;
1391+
1392+
class Foo extends React.Component {
1393+
state = { n: 0 };
1394+
render() {
1395+
instance = this;
1396+
return <div />;
1397+
}
1398+
}
1399+
1400+
ReactNoop.render(<Foo />);
1401+
ReactNoop.flush();
1402+
ops = [];
1403+
1404+
ReactNoop.syncUpdates(() => {
1405+
ReactNoop.batchedUpdates(() => {
1406+
instance.setState({ n: 1 }, () => ops.push('setState 1'));
1407+
instance.setState({ n: 2 }, () => ops.push('setState 2'));
1408+
ops.push('end batchedUpdates');
1409+
});
1410+
ops.push('end syncUpdates');
1411+
});
1412+
1413+
// ReactNoop.flush() not needed because updates are synchronous
1414+
1415+
expect(ops).toEqual([
1416+
'end batchedUpdates',
1417+
'setState 1',
1418+
'setState 2',
1419+
'end syncUpdates',
1420+
]);
1421+
expect(instance.state.n).toEqual(2);
1422+
});
1423+
1424+
it('can nest batchedUpdates', () => {
1425+
var ops = [];
1426+
var instance;
1427+
1428+
class Foo extends React.Component {
1429+
state = { n: 0 };
1430+
render() {
1431+
instance = this;
1432+
return <div />;
1433+
}
1434+
}
1435+
1436+
ReactNoop.render(<Foo />);
1437+
ReactNoop.flush();
1438+
ops = [];
1439+
1440+
ReactNoop.syncUpdates(() => {
1441+
ReactNoop.batchedUpdates(() => {
1442+
instance.setState({ n: 1 }, () => ops.push('setState 1'));
1443+
instance.setState({ n: 2 }, () => ops.push('setState 2'));
1444+
ReactNoop.batchedUpdates(() => {
1445+
instance.setState({ n: 3 }, () => ops.push('setState 3'));
1446+
instance.setState({ n: 4 }, () => ops.push('setState 4'));
1447+
ops.push('end inner batchedUpdates');
1448+
});
1449+
ops.push('end outer batchedUpdates');
1450+
});
1451+
ops.push('end syncUpdates');
1452+
});
1453+
1454+
// ReactNoop.flush() not needed because updates are synchronous
1455+
1456+
expect(ops).toEqual([
1457+
'end inner batchedUpdates',
1458+
'end outer batchedUpdates',
1459+
'setState 1',
1460+
'setState 2',
1461+
'setState 3',
1462+
'setState 4',
1463+
'end syncUpdates',
1464+
]);
1465+
expect(instance.state.n).toEqual(4);
1466+
});
13881467
});

0 commit comments

Comments
 (0)