mirror of
https://github.com/hanzoai/react.git
synced 2026-09-24 17:02:27 +00:00
Interactive updates shouldn't flush until the end of the outermost batch
Accounts for the case where an event is dispatched synchronously from inside another event, like `el.focus`. I've added a test, but in general we need more coverage around this area.
This commit is contained in:
+1
-10
@@ -5,11 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {
|
||||
batchedUpdates,
|
||||
flushInteractiveUpdates,
|
||||
interactiveUpdates,
|
||||
} from 'events/ReactGenericBatching';
|
||||
import {batchedUpdates, interactiveUpdates} from 'events/ReactGenericBatching';
|
||||
import {runExtractedEventsInBatch} from 'events/EventPluginHub';
|
||||
import {isFiberMounted} from 'react-reconciler/reflection';
|
||||
import {HostRoot} from 'shared/ReactTypeOfWork';
|
||||
@@ -166,11 +162,6 @@ export function trapCapturedEvent(topLevelType, handlerBaseName, element) {
|
||||
}
|
||||
|
||||
function dispatchInteractiveEvent(topLevelType, nativeEvent) {
|
||||
// If there are any pending interactive updates, synchronously flush them.
|
||||
// This needs to happen before we read any handlers, because the effect of the
|
||||
// previous event may affect which handlers are called during this event.
|
||||
flushInteractiveUpdates();
|
||||
// Increase the priority of updates inside this event.
|
||||
interactiveUpdates(dispatchEvent, topLevelType, nativeEvent);
|
||||
}
|
||||
|
||||
|
||||
@@ -167,6 +167,56 @@ describe('SimpleEventPlugin', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('batches updates that occur as a result of a nested event dispatch', () => {
|
||||
const container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
|
||||
let ops = [];
|
||||
let button;
|
||||
class Button extends React.Component {
|
||||
state = {count: 0};
|
||||
increment = () =>
|
||||
this.setState(state => ({
|
||||
count: state.count + 1,
|
||||
}));
|
||||
componentDidUpdate() {
|
||||
ops.push(`didUpdate - Count: ${this.state.count}`);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<button
|
||||
ref={el => (button = el)}
|
||||
onFocus={this.increment}
|
||||
onClick={() => {
|
||||
// The focus call synchronously dispatches a nested event. All of
|
||||
// the updates in this handler should be batched together.
|
||||
this.increment();
|
||||
button.focus();
|
||||
this.increment();
|
||||
}}>
|
||||
Count: {this.state.count}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function click() {
|
||||
button.dispatchEvent(
|
||||
new MouseEvent('click', {bubbles: true, cancelable: true}),
|
||||
);
|
||||
}
|
||||
|
||||
ReactDOM.render(<Button />, container);
|
||||
expect(button.textContent).toEqual('Count: 0');
|
||||
expect(ops).toEqual([]);
|
||||
|
||||
click();
|
||||
|
||||
// There should be exactly one update.
|
||||
expect(ops).toEqual(['didUpdate - Count: 3']);
|
||||
expect(button.textContent).toEqual('Count: 3');
|
||||
});
|
||||
|
||||
describe('interactive events, in async mode', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
|
||||
+19
-1
@@ -1807,13 +1807,31 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
|
||||
if (isBatchingInteractiveUpdates) {
|
||||
return fn(a, b);
|
||||
}
|
||||
|
||||
// If there are any pending interactive updates, synchronously flush them.
|
||||
// This needs to happen before we read any handlers, because the effect of
|
||||
// the previous event may influence which handlers are called during
|
||||
// this event.
|
||||
if (
|
||||
!isBatchingUpdates &&
|
||||
!isRendering &&
|
||||
lowestPendingInteractiveExpirationTime !== NoWork
|
||||
) {
|
||||
// Synchronously flush pending interactive updates.
|
||||
performWork(lowestPendingInteractiveExpirationTime, false, null);
|
||||
lowestPendingInteractiveExpirationTime = NoWork;
|
||||
}
|
||||
const previousIsBatchingInteractiveUpdates = isBatchingInteractiveUpdates;
|
||||
const previousIsBatchingUpdates = isBatchingUpdates;
|
||||
isBatchingInteractiveUpdates = true;
|
||||
isBatchingUpdates = true;
|
||||
try {
|
||||
return fn(a, b);
|
||||
} finally {
|
||||
isBatchingInteractiveUpdates = previousIsBatchingInteractiveUpdates;
|
||||
isBatchingUpdates = previousIsBatchingUpdates;
|
||||
if (!isBatchingUpdates && !isRendering) {
|
||||
performSyncWork();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user