Skip to content

Commit

Permalink
batchedUpdates tests
Browse files Browse the repository at this point in the history
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?
  • Loading branch information
acdlite committed Nov 2, 2016
1 parent cf8fe73 commit f52e512
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/renderers/noop/ReactNoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,10 @@ var ReactNoop = {
NoopRenderer.performWithPriority(AnimationPriority, fn);
},

batchedUpdates: NoopRenderer.batchedUpdates,

syncUpdates: NoopRenderer.syncUpdates,

// Logs the current state of the tree.
dumpTree(rootID : string = DEFAULT_ROOT_ID) {
const root = roots.get(rootID);
Expand Down
4 changes: 4 additions & 0 deletions src/renderers/shared/fiber/ReactFiberReconciler.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export type Reconciler<C, I, TI> = {
/* eslint-disable no-undef */
// FIXME: ESLint complains about type parameter
batchedUpdates<A>(fn : () => A) : A,
syncUpdates<A>(fn : () => A) : A,
/* eslint-enable no-undef */

// Used to extract the return value from the initial render. Legacy API.
Expand All @@ -86,6 +87,7 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) :
scheduleWork,
performWithPriority,
batchedUpdates,
syncUpdates,
} = ReactFiberScheduler(config);

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

batchedUpdates,

syncUpdates,

getPublicRootInstance(container : OpaqueNode) : (ReactComponent<any, any, any> | I | TI | null) {
const root : FiberRoot = (container.stateNode : any);
const containerFiber = root.current;
Expand Down
11 changes: 11 additions & 0 deletions src/renderers/shared/fiber/ReactFiberScheduler.js
Original file line number Diff line number Diff line change
Expand Up @@ -712,10 +712,21 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
}
}

function syncUpdates<A>(fn : () => A) : A {
const previousPriorityContext = priorityContext;
priorityContext = SynchronousPriority;
try {
return fn();
} finally {
priorityContext = previousPriorityContext;
}
}

return {
scheduleWork: scheduleWork,
scheduleDeferredWork: scheduleDeferredWork,
performWithPriority: performWithPriority,
batchedUpdates: batchedUpdates,
syncUpdates: syncUpdates,
};
};
79 changes: 79 additions & 0 deletions src/renderers/shared/fiber/__tests__/ReactIncremental-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1385,4 +1385,83 @@ describe('ReactIncremental', () => {
]);
});

it('performs batched updates at the end of the batch', () => {
var ops = [];
var instance;

class Foo extends React.Component {
state = { n: 0 };
render() {
instance = this;
return <div />;
}
}

ReactNoop.render(<Foo />);
ReactNoop.flush();
ops = [];

ReactNoop.syncUpdates(() => {
ReactNoop.batchedUpdates(() => {
instance.setState({ n: 1 }, () => ops.push('setState 1'));
instance.setState({ n: 2 }, () => ops.push('setState 2'));
ops.push('end batchedUpdates');
});
ops.push('end syncUpdates');
});

// ReactNoop.flush() not needed because updates are synchronous

expect(ops).toEqual([
'end batchedUpdates',
'setState 1',
'setState 2',
'end syncUpdates',
]);
expect(instance.state.n).toEqual(2);
});

it('can nest batchedUpdates', () => {
var ops = [];
var instance;

class Foo extends React.Component {
state = { n: 0 };
render() {
instance = this;
return <div />;
}
}

ReactNoop.render(<Foo />);
ReactNoop.flush();
ops = [];

ReactNoop.syncUpdates(() => {
ReactNoop.batchedUpdates(() => {
instance.setState({ n: 1 }, () => ops.push('setState 1'));
instance.setState({ n: 2 }, () => ops.push('setState 2'));
ReactNoop.batchedUpdates(() => {
instance.setState({ n: 3 }, () => ops.push('setState 3'));
instance.setState({ n: 4 }, () => ops.push('setState 4'));
ops.push('end inner batchedUpdates');
});
ops.push('end outer batchedUpdates');
});
ops.push('end syncUpdates');
});

// ReactNoop.flush() not needed because updates are synchronous

expect(ops).toEqual([
'end inner batchedUpdates',
'end outer batchedUpdates',
'setState 1',
'setState 2',
'setState 3',
'setState 4',
'end syncUpdates',
]);
expect(instance.state.n).toEqual(4);
});
});

0 comments on commit f52e512

Please sign in to comment.