diff --git a/packages/react-dom/src/__tests__/ReactDOMNestedEvents-test.js b/packages/react-dom/src/__tests__/ReactDOMNestedEvents-test.js
new file mode 100644
index 0000000000000..2de9a3127d331
--- /dev/null
+++ b/packages/react-dom/src/__tests__/ReactDOMNestedEvents-test.js
@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @emails react-core
+ */
+
+'use strict';
+
+describe('ReactDOMNestedEvents', () => {
+ let React;
+ let ReactDOM;
+ let Scheduler;
+ let TestUtils;
+ let act;
+ let useState;
+
+ beforeEach(() => {
+ jest.resetModules();
+ React = require('react');
+ ReactDOM = require('react-dom');
+ Scheduler = require('scheduler');
+ TestUtils = require('react-dom/test-utils');
+ act = TestUtils.unstable_concurrentAct;
+ useState = React.useState;
+ });
+
+ // @gate experimental
+ test('nested event dispatches should not cause updates to flush', async () => {
+ const buttonRef = React.createRef(null);
+ function App() {
+ const [isClicked, setIsClicked] = useState(false);
+ const [isFocused, setIsFocused] = useState(false);
+ const onClick = () => {
+ setIsClicked(true);
+ const el = buttonRef.current;
+ el.focus();
+ // The update triggered by the focus event should not have flushed yet.
+ // Nor the click update. They would have if we had wrapped the focus
+ // call in `flushSync`, though.
+ Scheduler.unstable_yieldValue(
+ 'Value right after focus call: ' + el.innerHTML,
+ );
+ };
+ const onFocus = () => {
+ setIsFocused(true);
+ };
+ return (
+ <>
+
+ >
+ );
+ }
+
+ const container = document.createElement('div');
+ document.body.appendChild(container);
+ const root = ReactDOM.unstable_createRoot(container);
+
+ await act(async () => {
+ root.render();
+ });
+ expect(buttonRef.current.innerHTML).toEqual(
+ 'Clicked: false, Focused: false',
+ );
+
+ await act(async () => {
+ buttonRef.current.click();
+ });
+ expect(Scheduler).toHaveYielded([
+ 'Value right after focus call: Clicked: false, Focused: false',
+ ]);
+ expect(buttonRef.current.innerHTML).toEqual('Clicked: true, Focused: true');
+ });
+});
diff --git a/packages/react-dom/src/events/ReactDOMEventListener.js b/packages/react-dom/src/events/ReactDOMEventListener.js
index f09d6ee864481..81b3a1b85dce4 100644
--- a/packages/react-dom/src/events/ReactDOMEventListener.js
+++ b/packages/react-dom/src/events/ReactDOMEventListener.js
@@ -25,21 +25,13 @@ import {
getSuspenseInstanceFromFiber,
} from 'react-reconciler/src/ReactFiberTreeReflection';
import {HostRoot, SuspenseComponent} from 'react-reconciler/src/ReactWorkTags';
-import {
- type EventSystemFlags,
- IS_CAPTURE_PHASE,
- IS_LEGACY_FB_SUPPORT_MODE,
-} from './EventSystemFlags';
+import {type EventSystemFlags, IS_CAPTURE_PHASE} from './EventSystemFlags';
import getEventTarget from './getEventTarget';
import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree';
-import {enableLegacyFBSupport} from 'shared/ReactFeatureFlags';
import {dispatchEventForPluginEventSystem} from './DOMPluginEventSystem';
-import {
- flushDiscreteUpdatesIfNeeded,
- discreteUpdates,
-} from './ReactDOMUpdateBatching';
+import {discreteUpdates} from './ReactDOMUpdateBatching';
import {
getCurrentPriorityLevel as getCurrentSchedulerPriorityLevel,
@@ -120,14 +112,6 @@ function dispatchDiscreteEvent(
container,
nativeEvent,
) {
- if (
- !enableLegacyFBSupport ||
- // If we are in Legacy FB support mode, it means we've already
- // flushed for this event and we don't need to do it again.
- (eventSystemFlags & IS_LEGACY_FB_SUPPORT_MODE) === 0
- ) {
- flushDiscreteUpdatesIfNeeded(nativeEvent.timeStamp);
- }
discreteUpdates(
dispatchEvent,
domEventName,
diff --git a/packages/react-dom/src/events/ReactDOMUpdateBatching.js b/packages/react-dom/src/events/ReactDOMUpdateBatching.js
index 283afb5b84d63..c768bce0c3a7b 100644
--- a/packages/react-dom/src/events/ReactDOMUpdateBatching.js
+++ b/packages/react-dom/src/events/ReactDOMUpdateBatching.js
@@ -88,13 +88,6 @@ export function discreteUpdates(fn, a, b, c, d) {
}
}
-// TODO: Replace with flushSync
-export function flushDiscreteUpdatesIfNeeded(timeStamp: number) {
- if (!isInsideEventHandler) {
- flushDiscreteUpdatesImpl();
- }
-}
-
export function setBatchingImplementation(
_batchedUpdatesImpl,
_discreteUpdatesImpl,
diff --git a/packages/react-dom/src/events/__tests__/DOMPluginEventSystem-test.internal.js b/packages/react-dom/src/events/__tests__/DOMPluginEventSystem-test.internal.js
index 3491aa7b94f1d..f5b8952a96efa 100644
--- a/packages/react-dom/src/events/__tests__/DOMPluginEventSystem-test.internal.js
+++ b/packages/react-dom/src/events/__tests__/DOMPluginEventSystem-test.internal.js
@@ -656,7 +656,7 @@ describe('DOMPluginEventSystem', () => {
document.body.removeChild(parentContainer);
});
- it('handle click events on dynamic portals', () => {
+ it('handle click events on dynamic portals', async () => {
const log = [];
function Parent() {
@@ -670,7 +670,7 @@ describe('DOMPluginEventSystem', () => {
ref.current,
),
);
- });
+ }, []);
return (
log.push('parent')} id="parent">
@@ -679,17 +679,25 @@ describe('DOMPluginEventSystem', () => {
);
}
- ReactDOM.render(
, container);
+ await act(async () => {
+ ReactDOM.render(
, container);
+ });
const parent = container.lastChild;
expect(parent.id).toEqual('parent');
- dispatchClickEvent(parent);
+
+ await act(async () => {
+ dispatchClickEvent(parent);
+ });
expect(log).toEqual(['parent']);
const child = parent.lastChild;
expect(child.id).toEqual('child');
- dispatchClickEvent(child);
+
+ await act(async () => {
+ dispatchClickEvent(child);
+ });
// we add both 'child' and 'parent' due to bubbling
expect(log).toEqual(['parent', 'child', 'parent']);
@@ -697,7 +705,7 @@ describe('DOMPluginEventSystem', () => {
// Slight alteration to the last test, to catch
// a subtle difference in traversal.
- it('handle click events on dynamic portals #2', () => {
+ it('handle click events on dynamic portals #2', async () => {
const log = [];
function Parent() {
@@ -711,7 +719,7 @@ describe('DOMPluginEventSystem', () => {
ref.current,
),
);
- });
+ }, []);
return (
log.push('parent')} id="parent">
@@ -720,17 +728,25 @@ describe('DOMPluginEventSystem', () => {
);
}
- ReactDOM.render(
, container);
+ await act(async () => {
+ ReactDOM.render(
, container);
+ });
const parent = container.lastChild;
expect(parent.id).toEqual('parent');
- dispatchClickEvent(parent);
+
+ await act(async () => {
+ dispatchClickEvent(parent);
+ });
expect(log).toEqual(['parent']);
const child = parent.lastChild;
expect(child.id).toEqual('child');
- dispatchClickEvent(child);
+
+ await act(async () => {
+ dispatchClickEvent(child);
+ });
// we add both 'child' and 'parent' due to bubbling
expect(log).toEqual(['parent', 'child', 'parent']);
diff --git a/packages/react-native-renderer/src/ReactFabric.js b/packages/react-native-renderer/src/ReactFabric.js
index 5f797c0dcfa39..9f1f44f51cf49 100644
--- a/packages/react-native-renderer/src/ReactFabric.js
+++ b/packages/react-native-renderer/src/ReactFabric.js
@@ -19,7 +19,6 @@ import {
batchedEventUpdates,
batchedUpdates as batchedUpdatesImpl,
discreteUpdates,
- flushDiscreteUpdates,
createContainer,
updateContainer,
injectIntoDevTools,
@@ -242,7 +241,6 @@ function createPortal(
setBatchingImplementation(
batchedUpdatesImpl,
discreteUpdates,
- flushDiscreteUpdates,
batchedEventUpdates,
);
diff --git a/packages/react-native-renderer/src/ReactNativeRenderer.js b/packages/react-native-renderer/src/ReactNativeRenderer.js
index 1e7b390f9ff38..de42c6e41a58c 100644
--- a/packages/react-native-renderer/src/ReactNativeRenderer.js
+++ b/packages/react-native-renderer/src/ReactNativeRenderer.js
@@ -19,7 +19,6 @@ import {
batchedUpdates as batchedUpdatesImpl,
batchedEventUpdates,
discreteUpdates,
- flushDiscreteUpdates,
createContainer,
updateContainer,
injectIntoDevTools,
@@ -241,7 +240,6 @@ function createPortal(
setBatchingImplementation(
batchedUpdatesImpl,
discreteUpdates,
- flushDiscreteUpdates,
batchedEventUpdates,
);
diff --git a/packages/react-native-renderer/src/legacy-events/ReactGenericBatching.js b/packages/react-native-renderer/src/legacy-events/ReactGenericBatching.js
index 89bbdd203b14d..3b30bb274ff1b 100644
--- a/packages/react-native-renderer/src/legacy-events/ReactGenericBatching.js
+++ b/packages/react-native-renderer/src/legacy-events/ReactGenericBatching.js
@@ -18,7 +18,6 @@ let batchedUpdatesImpl = function(fn, bookkeeping) {
let discreteUpdatesImpl = function(fn, a, b, c, d) {
return fn(a, b, c, d);
};
-let flushDiscreteUpdatesImpl = function() {};
let batchedEventUpdatesImpl = batchedUpdatesImpl;
let isInsideEventHandler = false;
@@ -59,25 +58,15 @@ export function discreteUpdates(fn, a, b, c, d) {
return discreteUpdatesImpl(fn, a, b, c, d);
} finally {
isInsideEventHandler = prevIsInsideEventHandler;
- if (!isInsideEventHandler) {
- }
- }
-}
-
-export function flushDiscreteUpdatesIfNeeded() {
- if (!isInsideEventHandler) {
- flushDiscreteUpdatesImpl();
}
}
export function setBatchingImplementation(
_batchedUpdatesImpl,
_discreteUpdatesImpl,
- _flushDiscreteUpdatesImpl,
_batchedEventUpdatesImpl,
) {
batchedUpdatesImpl = _batchedUpdatesImpl;
discreteUpdatesImpl = _discreteUpdatesImpl;
- flushDiscreteUpdatesImpl = _flushDiscreteUpdatesImpl;
batchedEventUpdatesImpl = _batchedEventUpdatesImpl;
}