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:
parent
d38cfd452f
commit
6da04b5d88
|
@ -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$"
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue