@samitouri / QOS-React / commits / 0a24255475

ReactDOM.activeUpdates (#10225)

ReactDOM.flushSync(batch)

Andrew Clark committed Jul 21, 2017 at 13:22 UTC 0a242554750bb288f88f62e9ed8541597baf9418
13 files changed +257 -153
scripts/fiber/tests-passing.txt
+4 -1
@@ -682,6 +682,10 @@ src/renderers/dom/fiber/__tests__/ReactDOMFiberAsync-test.js
682 * updates inside an async tree are async by default
683 * AsyncComponent creates an async subtree
684 * updates inside an async subtree are async by default
685 +* flushSync batches sync updates and flushes them at the end of the batch
686 +* flushSync flushes updates even if nested inside another flushSync
687 +* flushSync throws if already performing work
688 +* flushSync flushes updates before end of the tick
689
690 src/renderers/dom/shared/__tests__/CSSProperty-test.js
691 * should generate browser prefixes for its `isUnitlessNumber`
@@ -2160,7 +2164,6 @@ src/renderers/shared/fiber/__tests__/ReactIncremental-test.js
2164 * can call setState inside update callback
2165 * can replaceState
2166 * can forceUpdate
2163 -* performs batched updates at the end of the batch
2167 * can nest batchedUpdates
2168 * can handle if setState callback throws
2169 * merges and masks context
src/renderers/dom/fiber/ReactDOMFiberEntry.js
+2
@@ -742,6 +742,8 @@ var ReactDOMFiber = {
742
743 unstable_deferredUpdates: DOMRenderer.deferredUpdates,
744
745 + flushSync: DOMRenderer.flushSync,
746 +
747 __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
748 // For TapEventPlugin which is popular in open source
749 EventPluginHub: require('EventPluginHub'),
src/renderers/dom/fiber/__tests__/ReactDOMFiberAsync-test.js
+145
@@ -134,6 +134,151 @@ describe('ReactDOMFiberAsync', () => {
134 jest.runAllTimers();
135 expect(container.textContent).toEqual('1');
136 });
137 +
138 + it('flushSync batches sync updates and flushes them at the end of the batch', () => {
139 + let ops = [];
140 + let instance;
141 +
142 + class Component extends React.Component {
143 + state = {text: ''};
144 + push(val) {
145 + this.setState(state => ({text: state.text + val}));
146 + }
147 + componentDidUpdate() {
148 + ops.push(this.state.text);
149 + }
150 + render() {
151 + instance = this;
152 + return <span>{this.state.text}</span>;
153 + }
154 + }
155 +
156 + ReactDOM.render(<Component />, container);
157 +
158 + instance.push('A');
159 + expect(ops).toEqual(['A']);
160 + expect(container.textContent).toEqual('A');
161 +
162 + ReactDOM.flushSync(() => {
163 + instance.push('B');
164 + instance.push('C');
165 + // Not flushed yet
166 + expect(container.textContent).toEqual('A');
167 + expect(ops).toEqual(['A']);
168 + });
169 + expect(container.textContent).toEqual('ABC');
170 + expect(ops).toEqual(['A', 'ABC']);
171 + instance.push('D');
172 + expect(container.textContent).toEqual('ABCD');
173 + expect(ops).toEqual(['A', 'ABC', 'ABCD']);
174 + });
175 +
176 + it('flushSync flushes updates even if nested inside another flushSync', () => {
177 + let ops = [];
178 + let instance;
179 +
180 + class Component extends React.Component {
181 + state = {text: ''};
182 + push(val) {
183 + this.setState(state => ({text: state.text + val}));
184 + }
185 + componentDidUpdate() {
186 + ops.push(this.state.text);
187 + }
188 + render() {
189 + instance = this;
190 + return <span>{this.state.text}</span>;
191 + }
192 + }
193 +
194 + ReactDOM.render(<Component />, container);
195 +
196 + instance.push('A');
197 + expect(ops).toEqual(['A']);
198 + expect(container.textContent).toEqual('A');
199 +
200 + ReactDOM.flushSync(() => {
201 + instance.push('B');
202 + instance.push('C');
203 + // Not flushed yet
204 + expect(container.textContent).toEqual('A');
205 + expect(ops).toEqual(['A']);
206 +
207 + ReactDOM.flushSync(() => {
208 + instance.push('D');
209 + });
210 + // The nested flushSync caused everything to flush.
211 + expect(container.textContent).toEqual('ABCD');
212 + expect(ops).toEqual(['A', 'ABCD']);
213 + });
214 + expect(container.textContent).toEqual('ABCD');
215 + expect(ops).toEqual(['A', 'ABCD']);
216 + });
217 +
218 + it('flushSync throws if already performing work', () => {
219 + class Component extends React.Component {
220 + componentDidUpdate() {
221 + ReactDOM.flushSync(() => {});
222 + }
223 + render() {
224 + return null;
225 + }
226 + }
227 +
228 + // Initial mount
229 + ReactDOM.render(<Component />, container);
230 + // Update
231 + expect(() => ReactDOM.render(<Component />, container)).toThrow(
232 + 'flushSync was called from inside a lifecycle method',
233 + );
234 + });
235 +
236 + it('flushSync flushes updates before end of the tick', () => {
237 + let ops = [];
238 + let instance;
239 +
240 + class Component extends React.unstable_AsyncComponent {
241 + state = {text: ''};
242 + push(val) {
243 + this.setState(state => ({text: state.text + val}));
244 + }
245 + componentDidUpdate() {
246 + ops.push(this.state.text);
247 + }
248 + render() {
249 + instance = this;
250 + return <span>{this.state.text}</span>;
251 + }
252 + }
253 +
254 + ReactDOM.render(<Component />, container);
255 + jest.runAllTimers();
256 +
257 + // Updates are async by default
258 + instance.push('A');
259 + expect(ops).toEqual([]);
260 + expect(container.textContent).toEqual('');
261 +
262 + ReactDOM.flushSync(() => {
263 + instance.push('B');
264 + instance.push('C');
265 + // Not flushed yet
266 + expect(container.textContent).toEqual('');
267 + expect(ops).toEqual([]);
268 + });
269 + // Only the active updates have flushed
270 + expect(container.textContent).toEqual('BC');
271 + expect(ops).toEqual(['BC']);
272 +
273 + instance.push('D');
274 + expect(container.textContent).toEqual('BC');
275 + expect(ops).toEqual(['BC']);
276 +
277 + // Flush the async updates
278 + jest.runAllTimers();
279 + expect(container.textContent).toEqual('BCAD');
280 + expect(ops).toEqual(['BC', 'BCAD']);
281 + });
282 });
283 }
284 });
src/renderers/native/ReactNativeFiberEntry.js
+2
@@ -89,6 +89,8 @@ const ReactNativeFiber: ReactNativeType = {
89
90 unstable_batchedUpdates: ReactGenericBatching.batchedUpdates,
91
92 + flushSync: ReactNativeFiberRenderer.flushSync,
93 +
94 __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
95 // Used as a mixin in many createClass-based components
96 NativeMethodsMixin: require('NativeMethodsMixin'),
src/renderers/noop/ReactNoopEntry.js
+1 -1
@@ -361,7 +361,7 @@ var ReactNoop = {
361
362 unbatchedUpdates: NoopRenderer.unbatchedUpdates,
363
364 - syncUpdates: NoopRenderer.syncUpdates,
364 + flushSync: NoopRenderer.flushSync,
365
366 // Logs the current state of the tree.
367 dumpTree(rootID: string = DEFAULT_ROOT_ID) {
src/renderers/shared/fiber/ReactFiberReconciler.js
+4 -4
@@ -166,7 +166,7 @@ export type Reconciler<C, I, TI> = {
166 performWithPriority(priorityLevel: PriorityLevel, fn: Function): void,
167 batchedUpdates<A>(fn: () => A): A,
168 unbatchedUpdates<A>(fn: () => A): A,
169 - syncUpdates<A>(fn: () => A): A,
169 + flushSync<A>(fn: () => A): A,
170 deferredUpdates<A>(fn: () => A): A,
171
172 // Used to extract the return value from the initial render. Legacy API.
@@ -199,7 +199,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
199 performWithPriority,
200 batchedUpdates,
201 unbatchedUpdates,
202 - syncUpdates,
202 + flushSync,
203 deferredUpdates,
204 } = ReactFiberScheduler(config);
205
@@ -290,10 +290,10 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
290
291 unbatchedUpdates,
292
293 - syncUpdates,
294 -
293 deferredUpdates,
294
295 + flushSync,
296 +
297 getPublicRootInstance(
298 container: OpaqueRoot,
299 ): ReactComponent<any, any, any> | PI | null {
src/renderers/shared/fiber/ReactFiberScheduler.js
+13 -3
@@ -1497,13 +1497,23 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
1497 }
1498 }
1499
1500 - function syncUpdates<A>(fn: () => A): A {
1500 + function flushSync<A>(batch: () => A): A {
1501 + const previousIsBatchingUpdates = isBatchingUpdates;
1502 const previousPriorityContext = priorityContext;
1503 + isBatchingUpdates = true;
1504 priorityContext = SynchronousPriority;
1505 try {
1504 - return fn();
1506 + return batch();
1507 } finally {
1508 + isBatchingUpdates = previousIsBatchingUpdates;
1509 priorityContext = previousPriorityContext;
1510 +
1511 + invariant(
1512 + !isPerformingWork,
1513 + 'flushSync was called from inside a lifecycle method. It cannot be ' +
1514 + 'called when React is already rendering.',
1515 + );
1516 + performWork(TaskPriority, null);
1517 }
1518 }
1519
@@ -1523,7 +1533,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
1533 performWithPriority: performWithPriority,
1534 batchedUpdates: batchedUpdates,
1535 unbatchedUpdates: unbatchedUpdates,
1526 - syncUpdates: syncUpdates,
1536 + flushSync: flushSync,
1537 deferredUpdates: deferredUpdates,
1538 };
1539 };
src/renderers/shared/fiber/__tests__/ReactIncremental-test.js
+4 -42
@@ -326,7 +326,7 @@ describe('ReactIncremental', () => {
326 }
327
328 // Init
329 - ReactNoop.syncUpdates(() => {
329 + ReactNoop.flushSync(() => {
330 ReactNoop.render(<Foo text="foo" />);
331 });
332 ReactNoop.flush();
@@ -335,7 +335,7 @@ describe('ReactIncremental', () => {
335 ops = [];
336
337 // Render the high priority work (everying except the hidden trees).
338 - ReactNoop.syncUpdates(() => {
338 + ReactNoop.flushSync(() => {
339 ReactNoop.render(<Foo text="foo" />);
340 });
341 expect(ops).toEqual(['Foo', 'Bar', 'Bar']);
@@ -773,7 +773,7 @@ describe('ReactIncremental', () => {
773 // Interrupt the current low pri work with a high pri update elsewhere in
774 // the tree.
775 ops = [];
776 - ReactNoop.syncUpdates(() => {
776 + ReactNoop.flushSync(() => {
777 sibling.setState({});
778 });
779 expect(ops).toEqual(['Sibling']);
@@ -1661,42 +1661,6 @@ describe('ReactIncremental', () => {
1661 },
1662 );
1663
1664 - it('performs batched updates at the end of the batch', () => {
1665 - var ops = [];
1666 - var instance;
1667 -
1668 - class Foo extends React.Component {
1669 - state = {n: 0};
1670 - render() {
1671 - instance = this;
1672 - return <div />;
1673 - }
1674 - }
1675 -
1676 - ReactNoop.render(<Foo />);
1677 - ReactNoop.flush();
1678 - ops = [];
1679 -
1680 - ReactNoop.syncUpdates(() => {
1681 - ReactNoop.batchedUpdates(() => {
1682 - instance.setState({n: 1}, () => ops.push('setState 1'));
1683 - instance.setState({n: 2}, () => ops.push('setState 2'));
1684 - ops.push('end batchedUpdates');
1685 - });
1686 - ops.push('end syncUpdates');
1687 - });
1688 -
1689 - // ReactNoop.flush() not needed because updates are synchronous
1690 -
1691 - expect(ops).toEqual([
1692 - 'end batchedUpdates',
1693 - 'setState 1',
1694 - 'setState 2',
1695 - 'end syncUpdates',
1696 - ]);
1697 - expect(instance.state.n).toEqual(2);
1698 - });
1699 -
1664 it('can nest batchedUpdates', () => {
1665 var ops = [];
1666 var instance;
@@ -1713,7 +1677,7 @@ describe('ReactIncremental', () => {
1677 ReactNoop.flush();
1678 ops = [];
1679
1716 - ReactNoop.syncUpdates(() => {
1680 + ReactNoop.flushSync(() => {
1681 ReactNoop.batchedUpdates(() => {
1682 instance.setState({n: 1}, () => ops.push('setState 1'));
1683 instance.setState({n: 2}, () => ops.push('setState 2'));
@@ -1724,7 +1688,6 @@ describe('ReactIncremental', () => {
1688 });
1689 ops.push('end outer batchedUpdates');
1690 });
1727 - ops.push('end syncUpdates');
1691 });
1692
1693 // ReactNoop.flush() not needed because updates are synchronous
@@ -1736,7 +1699,6 @@ describe('ReactIncremental', () => {
1699 'setState 2',
1700 'setState 3',
1701 'setState 4',
1739 - 'end syncUpdates',
1702 ]);
1703 expect(instance.state.n).toEqual(4);
1704 });
src/renderers/shared/fiber/__tests__/ReactIncrementalErrorHandling-test.js
+25 -29
@@ -134,7 +134,7 @@ describe('ReactIncrementalErrorHandling', () => {
134 throw new Error('Hello');
135 }
136
137 - ReactNoop.syncUpdates(() => {
137 + ReactNoop.flushSync(() => {
138 ReactNoop.render(
139 <ErrorBoundary>
140 <BrokenRender />
@@ -176,19 +176,17 @@ describe('ReactIncrementalErrorHandling', () => {
176 throw new Error('Hello');
177 }
178
179 - ReactNoop.syncUpdates(() => {
180 - ReactNoop.batchedUpdates(() => {
181 - ReactNoop.render(
182 - <ErrorBoundary>
183 - Before the storm.
184 - </ErrorBoundary>,
185 - );
186 - ReactNoop.render(
187 - <ErrorBoundary>
188 - <BrokenRender />
189 - </ErrorBoundary>,
190 - );
191 - });
179 + ReactNoop.flushSync(() => {
180 + ReactNoop.render(
181 + <ErrorBoundary>
182 + Before the storm.
183 + </ErrorBoundary>,
184 + );
185 + ReactNoop.render(
186 + <ErrorBoundary>
187 + <BrokenRender />
188 + </ErrorBoundary>,
189 + );
190 });
191
192 expect(ops).toEqual([
@@ -292,7 +290,7 @@ describe('ReactIncrementalErrorHandling', () => {
290 }
291
292 expect(() => {
295 - ReactNoop.syncUpdates(() => {
293 + ReactNoop.flushSync(() => {
294 ReactNoop.render(
295 <RethrowErrorBoundary>
296 <BrokenRender />
@@ -327,19 +325,17 @@ describe('ReactIncrementalErrorHandling', () => {
325 }
326
327 expect(() => {
330 - ReactNoop.syncUpdates(() => {
331 - ReactNoop.batchedUpdates(() => {
332 - ReactNoop.render(
333 - <RethrowErrorBoundary>
334 - Before the storm.
335 - </RethrowErrorBoundary>,
336 - );
337 - ReactNoop.render(
338 - <RethrowErrorBoundary>
339 - <BrokenRender />
340 - </RethrowErrorBoundary>,
341 - );
342 - });
328 + ReactNoop.flushSync(() => {
329 + ReactNoop.render(
330 + <RethrowErrorBoundary>
331 + Before the storm.
332 + </RethrowErrorBoundary>,
333 + );
334 + ReactNoop.render(
335 + <RethrowErrorBoundary>
336 + <BrokenRender />
337 + </RethrowErrorBoundary>,
338 + );
339 });
340 }).toThrow('Hello');
341 expect(ops).toEqual([
@@ -383,7 +379,7 @@ describe('ReactIncrementalErrorHandling', () => {
379 it('applies sync updates regardless despite errors in scheduling', () => {
380 ReactNoop.render(<span prop="a:1" />);
381 expect(() => {
386 - ReactNoop.syncUpdates(() => {
382 + ReactNoop.flushSync(() => {
383 ReactNoop.batchedUpdates(() => {
384 ReactNoop.render(<span prop="a:2" />);
385 ReactNoop.render(<span prop="a:3" />);
src/renderers/shared/fiber/__tests__/ReactIncrementalPerf-test.js
+2 -2
@@ -282,7 +282,7 @@ describe('ReactDebugFiberPerf', () => {
282
283 it('measures deprioritized work', () => {
284 addComment('Flush the parent');
285 - ReactNoop.syncUpdates(() => {
285 + ReactNoop.flushSync(() => {
286 ReactNoop.render(
287 <Parent>
288 <div hidden={true}>
@@ -498,7 +498,7 @@ describe('ReactDebugFiberPerf', () => {
498 return <span prop={this.state.step} />;
499 }
500 }
501 - ReactNoop.syncUpdates(() => {
501 + ReactNoop.flushSync(() => {
502 ReactNoop.render(<Component />);
503 });
504 expect(getFlameChart()).toMatchSnapshot();
src/renderers/shared/fiber/__tests__/ReactIncrementalScheduling-test.js
+20 -24
@@ -56,7 +56,7 @@ describe('ReactIncrementalScheduling', () => {
56
57 ReactNoop.batchedUpdates(() => {
58 ReactNoop.render(<span prop={5} />);
59 - ReactNoop.syncUpdates(() => {
59 + ReactNoop.flushSync(() => {
60 ReactNoop.render(<span prop={2} />);
61 ReactNoop.render(<span prop={3} />);
62 ReactNoop.render(<span prop={4} />);
@@ -213,7 +213,7 @@ describe('ReactIncrementalScheduling', () => {
213 }
214 }
215
216 - ReactNoop.syncUpdates(() => {
216 + ReactNoop.flushSync(() => {
217 ReactNoop.render(<Foo />);
218 });
219 // The cDM update should not have flushed yet because it has async priority.
@@ -268,29 +268,25 @@ describe('ReactIncrementalScheduling', () => {
268 });
269
270 it('can opt-out of batching using unbatchedUpdates', () => {
271 - // syncUpdates gives synchronous priority to updates
272 - ReactNoop.syncUpdates(() => {
273 - // batchedUpdates downgrades sync updates to task priority
274 - ReactNoop.batchedUpdates(() => {
275 - ReactNoop.render(<span prop={0} />);
276 - expect(ReactNoop.getChildren()).toEqual([]);
277 - // Should not have flushed yet because we're still batching
278 -
279 - // unbatchedUpdates reverses the effect of batchedUpdates, so sync
280 - // updates are not batched
281 - ReactNoop.unbatchedUpdates(() => {
282 - ReactNoop.render(<span prop={1} />);
283 - expect(ReactNoop.getChildren()).toEqual([span(1)]);
284 - ReactNoop.render(<span prop={2} />);
285 - expect(ReactNoop.getChildren()).toEqual([span(2)]);
286 - });
287 -
288 - ReactNoop.render(<span prop={3} />);
271 + ReactNoop.flushSync(() => {
272 + ReactNoop.render(<span prop={0} />);
273 + expect(ReactNoop.getChildren()).toEqual([]);
274 + // Should not have flushed yet because we're still batching
275 +
276 + // unbatchedUpdates reverses the effect of batchedUpdates, so sync
277 + // updates are not batched
278 + ReactNoop.unbatchedUpdates(() => {
279 + ReactNoop.render(<span prop={1} />);
280 + expect(ReactNoop.getChildren()).toEqual([span(1)]);
281 + ReactNoop.render(<span prop={2} />);
282 expect(ReactNoop.getChildren()).toEqual([span(2)]);
283 });
291 - // Remaining update is now flushed
292 - expect(ReactNoop.getChildren()).toEqual([span(3)]);
284 +
285 + ReactNoop.render(<span prop={3} />);
286 + expect(ReactNoop.getChildren()).toEqual([span(2)]);
287 });
288 + // Remaining update is now flushed
289 + expect(ReactNoop.getChildren()).toEqual([span(3)]);
290 });
291
292 it('nested updates are always deferred, even inside unbatchedUpdates', () => {
@@ -320,10 +316,10 @@ describe('ReactIncrementalScheduling', () => {
316 ReactNoop.flush();
317 expect(ReactNoop.getChildren()).toEqual([span(0)]);
318
323 - ReactNoop.syncUpdates(() => {
319 + ReactNoop.flushSync(() => {
320 instance.setState({step: 1});
325 - expect(ReactNoop.getChildren()).toEqual([span(2)]);
321 });
322 + expect(ReactNoop.getChildren()).toEqual([span(2)]);
323
324 expect(ops).toEqual([
325 'render: 0',
src/renderers/shared/fiber/__tests__/ReactIncrementalTriangle-test.js
+8 -12
@@ -79,17 +79,13 @@ describe('ReactIncrementalTriangle', () => {
79 if (this.props.depth !== 0) {
80 throw new Error('Cannot activate non-leaf component');
81 }
82 - ReactNoop.syncUpdates(() => {
83 - this.setState({isActive: true});
84 - });
82 + this.setState({isActive: true});
83 }
84 deactivate() {
85 if (this.props.depth !== 0) {
86 throw new Error('Cannot deactivate non-leaf component');
87 }
90 - ReactNoop.syncUpdates(() => {
91 - this.setState({isActive: false});
92 - });
88 + this.setState({isActive: false});
89 }
90 shouldComponentUpdate(nextProps, nextState) {
91 return (
@@ -121,9 +117,7 @@ describe('ReactIncrementalTriangle', () => {
117 state = {counter: 0};
118 interrupt() {
119 // Triggers a restart from the top.
124 - ReactNoop.syncUpdates(() => {
125 - this.forceUpdate();
126 - });
120 + this.forceUpdate();
121 }
122 setCounter(counter) {
123 const currentCounter = this.state.counter;
@@ -193,15 +187,17 @@ describe('ReactIncrementalTriangle', () => {
187
188 let activeTriangle = null;
189 for (var i = 0; i < actions.length; i++) {
196 - ReactNoop.batchedUpdates(() => {
190 + ReactNoop.flushSync(() => {
191 const action = actions[i];
192 switch (action.type) {
193 case FLUSH:
194 ReactNoop.flushUnitsOfWork(action.unitsOfWork);
195 break;
196 case STEP:
203 - app.setCounter(action.counter);
204 - expectedCounterAtEnd = action.counter;
197 + ReactNoop.deferredUpdates(() => {
198 + app.setCounter(action.counter);
199 + expectedCounterAtEnd = action.counter;
200 + });
201 break;
202 case INTERRUPT:
203 app.interrupt();
src/renderers/shared/fiber/__tests__/ReactIncrementalUpdates-test.js
+27 -35
@@ -96,18 +96,16 @@ describe('ReactIncrementalUpdates', () => {
96 ReactNoop.render(<Foo />);
97 ReactNoop.flush();
98
99 - ReactNoop.syncUpdates(() => {
100 - ReactNoop.batchedUpdates(() => {
101 - ReactNoop.deferredUpdates(() => {
102 - instance.setState({x: 'x'});
103 - instance.setState({y: 'y'});
104 - });
105 - instance.setState({a: 'a'});
106 - instance.setState({b: 'b'});
107 - ReactNoop.deferredUpdates(() => {
108 - instance.updater.enqueueReplaceState(instance, {c: 'c'});
109 - instance.setState({d: 'd'});
110 - });
99 + ReactNoop.flushSync(() => {
100 + ReactNoop.deferredUpdates(() => {
101 + instance.setState({x: 'x'});
102 + instance.setState({y: 'y'});
103 + });
104 + instance.setState({a: 'a'});
105 + instance.setState({b: 'b'});
106 + ReactNoop.deferredUpdates(() => {
107 + instance.updater.enqueueReplaceState(instance, {c: 'c'});
108 + instance.setState({d: 'd'});
109 });
110 });
111
@@ -160,15 +158,13 @@ describe('ReactIncrementalUpdates', () => {
158 ReactNoop.flushThrough(['a', 'b', 'c']);
159 expect(ReactNoop.getChildren()).toEqual([span('')]);
160
163 - // Schedule some more updates at different priorities
164 - ReactNoop.batchedUpdates(() => {
165 - instance.setState(createUpdate('f'));
166 - ReactNoop.syncUpdates(() => {
167 - instance.setState(createUpdate('d'));
168 - instance.setState(createUpdate('e'));
169 - });
170 - instance.setState(createUpdate('g'));
161 + // Schedule some more updates at different priorities{
162 + instance.setState(createUpdate('f'));
163 + ReactNoop.flushSync(() => {
164 + instance.setState(createUpdate('d'));
165 + instance.setState(createUpdate('e'));
166 });
167 + instance.setState(createUpdate('g'));
168
169 // The sync updates should have flushed, but not the async ones
170 expect(ReactNoop.getChildren()).toEqual([span('de')]);
@@ -210,17 +206,15 @@ describe('ReactIncrementalUpdates', () => {
206 ReactNoop.flushThrough(['a', 'b', 'c']);
207 expect(ReactNoop.getChildren()).toEqual([span('')]);
208
213 - // Schedule some more updates at different priorities
214 - ReactNoop.batchedUpdates(() => {
215 - instance.setState(createUpdate('f'));
216 - ReactNoop.syncUpdates(() => {
217 - instance.setState(createUpdate('d'));
218 - // No longer a public API, but we can test that it works internally by
219 - // reaching into the updater.
220 - instance.updater.enqueueReplaceState(instance, createUpdate('e'));
221 - });
222 - instance.setState(createUpdate('g'));
209 + // Schedule some more updates at different priorities{
210 + instance.setState(createUpdate('f'));
211 + ReactNoop.flushSync(() => {
212 + instance.setState(createUpdate('d'));
213 + // No longer a public API, but we can test that it works internally by
214 + // reaching into the updater.
215 + instance.updater.enqueueReplaceState(instance, createUpdate('e'));
216 });
217 + instance.setState(createUpdate('g'));
218
219 // The sync updates should have flushed, but not the async ones. Update d
220 // was dropped and replaced by e.
@@ -307,11 +301,9 @@ describe('ReactIncrementalUpdates', () => {
301
302 ops = [];
303
310 - ReactNoop.batchedUpdates(() => {
311 - ReactNoop.syncUpdates(() => {
312 - instance.setState({a: 'a'});
313 - ReactNoop.render(<Foo />); // Trigger componentWillReceiveProps
314 - });
304 + ReactNoop.flushSync(() => {
305 + instance.setState({a: 'a'});
306 + ReactNoop.render(<Foo />); // Trigger componentWillReceiveProps
307 });
308
309 expect(instance.state).toEqual({a: 'a', b: 'b'});