mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Use unbatchedUpdates to opt-out of batching
Reverses the effect of batchedUpdates by resetting the current batching context. Does not affect nested updates, which are always deferred regardless of whether they are inside a batch.
This commit is contained in:
@@ -1207,6 +1207,8 @@ src/renderers/shared/fiber/__tests__/ReactIncrementalScheduling-test.js
|
||||
* can opt-in to deferred/animation scheduling inside componentDidMount/Update
|
||||
* performs Task work even after time runs out
|
||||
* does not perform animation work after time runs out
|
||||
* can opt-out of batching using unbatchedUpdates
|
||||
* nested updates are always deferred, even inside unbatchedUpdates
|
||||
|
||||
src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js
|
||||
* can update child nodes of a host instance
|
||||
|
||||
@@ -326,8 +326,8 @@ function renderSubtreeIntoContainer(parentComponent : ?ReactComponent<any, any,
|
||||
}
|
||||
const newRoot = DOMRenderer.createContainer(container);
|
||||
root = container._reactRootContainer = newRoot;
|
||||
// Initial mount is always sync, even if we're in a batch.
|
||||
DOMRenderer.syncUpdates(() => {
|
||||
// Initial mount should not be batched.
|
||||
DOMRenderer.unbatchedUpdates(() => {
|
||||
DOMRenderer.updateContainer(children, newRoot, parentComponent, callback);
|
||||
});
|
||||
} else {
|
||||
@@ -354,8 +354,8 @@ var ReactDOM = {
|
||||
unmountComponentAtNode(container : DOMContainerElement) {
|
||||
warnAboutUnstableUse();
|
||||
if (container._reactRootContainer) {
|
||||
// Unmount is always sync, even if we're in a batch.
|
||||
return DOMRenderer.syncUpdates(() => {
|
||||
// Unmount should not be batched.
|
||||
return DOMRenderer.unbatchedUpdates(() => {
|
||||
return renderSubtreeIntoContainer(null, null, container, () => {
|
||||
container._reactRootContainer = null;
|
||||
});
|
||||
|
||||
@@ -264,6 +264,8 @@ var ReactNoop = {
|
||||
|
||||
batchedUpdates: NoopRenderer.batchedUpdates,
|
||||
|
||||
unbatchedUpdates: NoopRenderer.unbatchedUpdates,
|
||||
|
||||
syncUpdates: NoopRenderer.syncUpdates,
|
||||
|
||||
// Logs the current state of the tree.
|
||||
|
||||
@@ -82,6 +82,7 @@ export type Reconciler<C, I, TI> = {
|
||||
/* eslint-disable no-undef */
|
||||
// FIXME: ESLint complains about type parameter
|
||||
batchedUpdates<A>(fn : () => A) : A,
|
||||
unbatchedUpdates<A>(fn : () => A) : A,
|
||||
syncUpdates<A>(fn : () => A) : A,
|
||||
deferredUpdates<A>(fn : () => A) : A,
|
||||
/* eslint-enable no-undef */
|
||||
@@ -107,6 +108,7 @@ module.exports = function<T, P, I, TI, C, CX, CI>(config : HostConfig<T, P, I, T
|
||||
getPriorityContext,
|
||||
performWithPriority,
|
||||
batchedUpdates,
|
||||
unbatchedUpdates,
|
||||
syncUpdates,
|
||||
deferredUpdates,
|
||||
} = ReactFiberScheduler(config);
|
||||
@@ -161,6 +163,8 @@ module.exports = function<T, P, I, TI, C, CX, CI>(config : HostConfig<T, P, I, T
|
||||
|
||||
batchedUpdates,
|
||||
|
||||
unbatchedUpdates,
|
||||
|
||||
syncUpdates,
|
||||
|
||||
deferredUpdates,
|
||||
|
||||
@@ -1094,19 +1094,26 @@ module.exports = function<T, P, I, TI, C, CX, CI>(config : HostConfig<T, P, I, T
|
||||
}
|
||||
}
|
||||
|
||||
function syncUpdates<A>(fn : () => A) : A {
|
||||
const previousPriorityContext = priorityContext;
|
||||
function unbatchedUpdates<A>(fn : () => A) : A {
|
||||
const previousIsBatchingUpdates = isBatchingUpdates;
|
||||
priorityContext = SynchronousPriority;
|
||||
isBatchingUpdates = false;
|
||||
try {
|
||||
return fn();
|
||||
} finally {
|
||||
priorityContext = previousPriorityContext;
|
||||
isBatchingUpdates = previousIsBatchingUpdates;
|
||||
}
|
||||
}
|
||||
|
||||
function syncUpdates<A>(fn : () => A) : A {
|
||||
const previousPriorityContext = priorityContext;
|
||||
priorityContext = SynchronousPriority;
|
||||
try {
|
||||
return fn();
|
||||
} finally {
|
||||
priorityContext = previousPriorityContext;
|
||||
}
|
||||
}
|
||||
|
||||
function deferredUpdates<A>(fn : () => A) : A {
|
||||
const previousPriorityContext = priorityContext;
|
||||
priorityContext = LowPriority;
|
||||
@@ -1122,6 +1129,7 @@ module.exports = function<T, P, I, TI, C, CX, CI>(config : HostConfig<T, P, I, T
|
||||
getPriorityContext: getPriorityContext,
|
||||
performWithPriority: performWithPriority,
|
||||
batchedUpdates: batchedUpdates,
|
||||
unbatchedUpdates: unbatchedUpdates,
|
||||
syncUpdates: syncUpdates,
|
||||
deferredUpdates: deferredUpdates,
|
||||
};
|
||||
|
||||
@@ -340,4 +340,71 @@ describe('ReactIncrementalScheduling', () => {
|
||||
// animation priority.
|
||||
expect(ReactNoop.getChildren()).toEqual([span(1)]);
|
||||
});
|
||||
|
||||
it('can opt-out of batching using unbatchedUpdates', () => {
|
||||
// syncUpdates gives synchronous priority to updates
|
||||
ReactNoop.syncUpdates(() => {
|
||||
// batchedUpdates downgrades sync updates to task priority
|
||||
ReactNoop.batchedUpdates(() => {
|
||||
ReactNoop.render(<span prop={0} />);
|
||||
expect(ReactNoop.getChildren()).toEqual([]);
|
||||
// Should not have flushed yet because we're still batching
|
||||
|
||||
// unbatchedUpdates reverses the effect of batchedUpdates, so sync
|
||||
// updates are not batched
|
||||
ReactNoop.unbatchedUpdates(() => {
|
||||
ReactNoop.render(<span prop={1} />);
|
||||
expect(ReactNoop.getChildren()).toEqual([span(1)]);
|
||||
ReactNoop.render(<span prop={2} />);
|
||||
expect(ReactNoop.getChildren()).toEqual([span(2)]);
|
||||
});
|
||||
|
||||
ReactNoop.render(<span prop={3} />);
|
||||
expect(ReactNoop.getChildren()).toEqual([span(2)]);
|
||||
});
|
||||
// Remaining update is now flushed
|
||||
expect(ReactNoop.getChildren()).toEqual([span(3)]);
|
||||
});
|
||||
});
|
||||
|
||||
it('nested updates are always deferred, even inside unbatchedUpdates', () => {
|
||||
let instance;
|
||||
let ops = [];
|
||||
class Foo extends React.Component {
|
||||
state = { step: 0 };
|
||||
componentDidUpdate() {
|
||||
ops.push('componentDidUpdate: ' + this.state.step);
|
||||
if (this.state.step === 1) {
|
||||
ReactNoop.unbatchedUpdates(() => {
|
||||
// This is a nested state update, so it should not be
|
||||
// flushed synchronously, even though we wrapped it
|
||||
// in unbatchedUpdates.
|
||||
this.setState({ step: 2 });
|
||||
});
|
||||
expect(ReactNoop.getChildren()).toEqual([span(1)]);
|
||||
}
|
||||
}
|
||||
render() {
|
||||
ops.push('render: ' + this.state.step);
|
||||
instance = this;
|
||||
return <span prop={this.state.step} />;
|
||||
}
|
||||
}
|
||||
ReactNoop.render(<Foo />);
|
||||
ReactNoop.flush();
|
||||
expect(ReactNoop.getChildren()).toEqual([span(0)]);
|
||||
|
||||
ReactNoop.syncUpdates(() => {
|
||||
instance.setState({ step: 1 });
|
||||
expect(ReactNoop.getChildren()).toEqual([span(2)]);
|
||||
});
|
||||
|
||||
expect(ops).toEqual([
|
||||
'render: 0',
|
||||
'render: 1',
|
||||
'componentDidUpdate: 1',
|
||||
'render: 2',
|
||||
'componentDidUpdate: 2',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user