Fix interaction tracing for batched update mounts (#15567)

* Added failing test for act+interaction tracing
* Mark pending interactions on root for legacy unbatched phase
This commit is contained in:
Brian Vaughn 2019-05-06 12:59:48 -07:00 committed by GitHub
parent d38cfd452f
commit 6da04b5d88
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 164 additions and 1 deletions

View File

@ -87,7 +87,7 @@
"@mattiasbuelens/web-streams-polyfill": "0.1.0"
},
"devEngines": {
"node": "8.x || 9.x || 10.x || 11.x"
"node": "8.x || 9.x || 10.x || 11.x || 12.x"
},
"jest": {
"testRegex": "/scripts/jest/dont-run-jest-directly\\.js$"

View File

@ -10,6 +10,7 @@
let React;
let ReactDOM;
let ReactTestUtils;
let SchedulerTracing;
let act;
let container;
@ -29,6 +30,7 @@ describe('ReactTestUtils.act()', () => {
React = require('react');
ReactDOM = require('react-dom');
ReactTestUtils = require('react-dom/test-utils');
SchedulerTracing = require('scheduler/tracing');
act = ReactTestUtils.act;
container = document.createElement('div');
document.body.appendChild(container);
@ -423,4 +425,82 @@ describe('ReactTestUtils.act()', () => {
expect(el.innerHTML).toBe('5');
});
});
describe('interaction tracing', () => {
if (__DEV__) {
it('should correctly trace interactions for sync roots', () => {
let expectedInteraction;
const Component = jest.fn(() => {
expect(expectedInteraction).toBeDefined();
const interactions = SchedulerTracing.unstable_getCurrent();
expect(interactions.size).toBe(1);
expect(interactions).toContain(expectedInteraction);
return null;
});
act(() => {
SchedulerTracing.unstable_trace(
'mount traced inside act',
performance.now(),
() => {
const interactions = SchedulerTracing.unstable_getCurrent();
expect(interactions.size).toBe(1);
expectedInteraction = Array.from(interactions)[0];
ReactDOM.render(<Component />, container);
},
);
});
act(() => {
SchedulerTracing.unstable_trace(
'update traced inside act',
performance.now(),
() => {
const interactions = SchedulerTracing.unstable_getCurrent();
expect(interactions.size).toBe(1);
expectedInteraction = Array.from(interactions)[0];
ReactDOM.render(<Component />, container);
},
);
});
const secondContainer = document.createElement('div');
SchedulerTracing.unstable_trace(
'mount traced outside act',
performance.now(),
() => {
act(() => {
const interactions = SchedulerTracing.unstable_getCurrent();
expect(interactions.size).toBe(1);
expectedInteraction = Array.from(interactions)[0];
ReactDOM.render(<Component />, secondContainer);
});
},
);
SchedulerTracing.unstable_trace(
'update traced outside act',
performance.now(),
() => {
act(() => {
const interactions = SchedulerTracing.unstable_getCurrent();
expect(interactions.size).toBe(1);
expectedInteraction = Array.from(interactions)[0];
ReactDOM.render(<Component />, secondContainer);
});
},
);
expect(Component).toHaveBeenCalledTimes(4);
});
}
});
});

View File

@ -1618,4 +1618,84 @@ describe('ReactUpdates', () => {
expect(container.textContent).toBe('1000');
});
}
if (__DEV__) {
it('should properly trace interactions within batched udpates', () => {
const SchedulerTracing = require('scheduler/tracing');
let expectedInteraction;
const container = document.createElement('div');
const Component = jest.fn(() => {
expect(expectedInteraction).toBeDefined();
const interactions = SchedulerTracing.unstable_getCurrent();
expect(interactions.size).toBe(1);
expect(interactions).toContain(expectedInteraction);
return null;
});
ReactDOM.unstable_batchedUpdates(() => {
SchedulerTracing.unstable_trace(
'mount traced inside a batched update',
1,
() => {
const interactions = SchedulerTracing.unstable_getCurrent();
expect(interactions.size).toBe(1);
expectedInteraction = Array.from(interactions)[0];
ReactDOM.render(<Component />, container);
},
);
});
ReactDOM.unstable_batchedUpdates(() => {
SchedulerTracing.unstable_trace(
'update traced inside a batched update',
2,
() => {
const interactions = SchedulerTracing.unstable_getCurrent();
expect(interactions.size).toBe(1);
expectedInteraction = Array.from(interactions)[0];
ReactDOM.render(<Component />, container);
},
);
});
const secondContainer = document.createElement('div');
SchedulerTracing.unstable_trace(
'mount traced outside a batched update',
3,
() => {
ReactDOM.unstable_batchedUpdates(() => {
const interactions = SchedulerTracing.unstable_getCurrent();
expect(interactions.size).toBe(1);
expectedInteraction = Array.from(interactions)[0];
ReactDOM.render(<Component />, secondContainer);
});
},
);
SchedulerTracing.unstable_trace(
'update traced outside a batched update',
4,
() => {
ReactDOM.unstable_batchedUpdates(() => {
const interactions = SchedulerTracing.unstable_getCurrent();
expect(interactions.size).toBe(1);
expectedInteraction = Array.from(interactions)[0];
ReactDOM.render(<Component />, container);
});
},
);
expect(Component).toHaveBeenCalledTimes(4);
});
}
});

View File

@ -331,6 +331,9 @@ export function scheduleUpdateOnFiber(
if (expirationTime === Sync) {
if (workPhase === LegacyUnbatchedPhase) {
// Register pending interactions on the root to avoid losing traced interaction data.
schedulePendingInteraction(root, expirationTime);
// This is a legacy edge case. The initial mount of a ReactDOM.render-ed
// root inside of batchedUpdates should be synchronous, but layout updates
// should be deferred until the end of the batch.