Skip to content

Commit fc80772

Browse files
authored
[react-events] Ensure updateEventListeners updates in commit phase (#16540)
1 parent 0f6e3cd commit fc80772

File tree

10 files changed

+261
-249
lines changed

10 files changed

+261
-249
lines changed

packages/react-art/src/ReactARTHostConfig.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,6 @@ export function mountResponderInstance(
432432
props: Object,
433433
state: Object,
434434
instance: Object,
435-
rootContainerInstance: Object,
436435
) {
437436
throw new Error('Not yet implemented.');
438437
}

packages/react-dom/src/client/ReactDOMHostConfig.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -824,10 +824,9 @@ export function mountResponderInstance(
824824
responderProps: Object,
825825
responderState: Object,
826826
instance: Instance,
827-
rootContainerInstance: Container,
828827
): ReactDOMEventResponderInstance {
829828
// Listen to events
830-
const doc = rootContainerInstance.ownerDocument;
829+
const doc = instance.ownerDocument;
831830
const documentBody = doc.body || doc;
832831
const {
833832
rootEventTypes,

packages/react-dom/src/events/DOMEventResponderSystem.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
PASSIVE_NOT_SUPPORTED,
1313
} from 'legacy-events/EventSystemFlags';
1414
import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
15-
import {HostComponent} from 'shared/ReactWorkTags';
15+
import {HostComponent, SuspenseComponent} from 'shared/ReactWorkTags';
1616
import type {EventPriority} from 'shared/ReactTypes';
1717
import type {
1818
ReactDOMEventResponder,
@@ -32,10 +32,6 @@ import type {Fiber} from 'react-reconciler/src/ReactFiber';
3232
import warning from 'shared/warning';
3333
import {enableFlareAPI} from 'shared/ReactFeatureFlags';
3434
import invariant from 'shared/invariant';
35-
import {
36-
isFiberSuspenseAndTimedOut,
37-
getSuspenseFallbackChild,
38-
} from 'react-reconciler/src/ReactFiberEvents';
3935

4036
import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree';
4137
import {
@@ -630,6 +626,14 @@ function validateResponderContext(): void {
630626
);
631627
}
632628

629+
function isFiberSuspenseAndTimedOut(fiber: Fiber): boolean {
630+
return fiber.tag === SuspenseComponent && fiber.memoizedState !== null;
631+
}
632+
633+
function getSuspenseFallbackChild(fiber: Fiber): Fiber | null {
634+
return ((((fiber.child: any): Fiber).sibling: any): Fiber).child;
635+
}
636+
633637
export function dispatchEventForResponderEventSystem(
634638
topLevelType: string,
635639
targetFiber: null | Fiber,

packages/react-dom/src/events/__tests__/DOMEventResponderSystem-test.internal.js

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ let ReactFeatureFlags;
1414
let ReactDOM;
1515
let ReactDOMServer;
1616
let ReactTestRenderer;
17+
let Scheduler;
1718

1819
// FIXME: What should the public API be for setting an event's priority? Right
1920
// now it's an enum but is that what we want? Hard coding this for now.
@@ -72,6 +73,7 @@ describe('DOMEventResponderSystem', () => {
7273
React = require('react');
7374
ReactDOM = require('react-dom');
7475
ReactDOMServer = require('react-dom/server');
76+
Scheduler = require('scheduler');
7577
container = document.createElement('div');
7678
document.body.appendChild(container);
7779
});
@@ -811,8 +813,8 @@ describe('DOMEventResponderSystem', () => {
811813

812814
it('the event responder system should warn on accessing invalid properties', () => {
813815
const TestResponder = createEventResponder({
814-
rootEventTypes: ['click'],
815-
onRootEvent: (event, context, props) => {
816+
targetEventTypes: ['click'],
817+
onEvent: (event, context, props) => {
816818
const syntheticEvent = {
817819
target: event.target,
818820
type: 'click',
@@ -823,19 +825,24 @@ describe('DOMEventResponderSystem', () => {
823825
});
824826

825827
let handler;
828+
let buttonRef = React.createRef();
826829
const Test = () => {
827830
const listener = React.unstable_useResponder(TestResponder, {
828831
onClick: handler,
829832
});
830833

831-
return <button listeners={listener}>Click me!</button>;
834+
return (
835+
<button listeners={listener} ref={buttonRef}>
836+
Click me!
837+
</button>
838+
);
832839
};
833840
expect(() => {
834841
handler = event => {
835842
event.preventDefault();
836843
};
837844
ReactDOM.render(<Test />, container);
838-
dispatchClickEvent(document.body);
845+
dispatchClickEvent(buttonRef.current);
839846
}).toWarnDev(
840847
'Warning: preventDefault() is not available on event objects created from event responder modules ' +
841848
'(React Flare).' +
@@ -847,7 +854,7 @@ describe('DOMEventResponderSystem', () => {
847854
event.stopPropagation();
848855
};
849856
ReactDOM.render(<Test />, container);
850-
dispatchClickEvent(document.body);
857+
dispatchClickEvent(buttonRef.current);
851858
}).toWarnDev(
852859
'Warning: stopPropagation() is not available on event objects created from event responder modules ' +
853860
'(React Flare).' +
@@ -859,7 +866,7 @@ describe('DOMEventResponderSystem', () => {
859866
event.isDefaultPrevented();
860867
};
861868
ReactDOM.render(<Test />, container);
862-
dispatchClickEvent(document.body);
869+
dispatchClickEvent(buttonRef.current);
863870
}).toWarnDev(
864871
'Warning: isDefaultPrevented() is not available on event objects created from event responder modules ' +
865872
'(React Flare).' +
@@ -871,7 +878,7 @@ describe('DOMEventResponderSystem', () => {
871878
event.isPropagationStopped();
872879
};
873880
ReactDOM.render(<Test />, container);
874-
dispatchClickEvent(document.body);
881+
dispatchClickEvent(buttonRef.current);
875882
}).toWarnDev(
876883
'Warning: isPropagationStopped() is not available on event objects created from event responder modules ' +
877884
'(React Flare).' +
@@ -883,7 +890,7 @@ describe('DOMEventResponderSystem', () => {
883890
return event.nativeEvent;
884891
};
885892
ReactDOM.render(<Test />, container);
886-
dispatchClickEvent(document.body);
893+
dispatchClickEvent(buttonRef.current);
887894
}).toWarnDev(
888895
'Warning: nativeEvent is not available on event objects created from event responder modules ' +
889896
'(React Flare).' +
@@ -934,4 +941,57 @@ describe('DOMEventResponderSystem', () => {
934941
ReactDOM.render(<Test2 />, container);
935942
buttonRef.current.dispatchEvent(createEvent('foobar'));
936943
});
944+
945+
it('should work with concurrent mode updates', async () => {
946+
const log = [];
947+
const TestResponder = createEventResponder({
948+
targetEventTypes: ['click'],
949+
onEvent(event, context, props) {
950+
log.push(props);
951+
},
952+
});
953+
const ref = React.createRef();
954+
955+
function Test({counter}) {
956+
const listener = React.unstable_useResponder(TestResponder, {counter});
957+
958+
return (
959+
<button listeners={listener} ref={ref}>
960+
Press me
961+
</button>
962+
);
963+
}
964+
965+
let root = ReactDOM.unstable_createRoot(container);
966+
let batch = root.createBatch();
967+
batch.render(<Test counter={0} />);
968+
Scheduler.unstable_flushAll();
969+
jest.runAllTimers();
970+
batch.commit();
971+
972+
// Click the button
973+
dispatchClickEvent(ref.current);
974+
expect(log).toEqual([{counter: 0}]);
975+
976+
// Clear log
977+
log.length = 0;
978+
979+
// Increase counter
980+
batch = root.createBatch();
981+
batch.render(<Test counter={1} />);
982+
Scheduler.unstable_flushAll();
983+
jest.runAllTimers();
984+
985+
// Click the button again
986+
dispatchClickEvent(ref.current);
987+
expect(log).toEqual([{counter: 0}]);
988+
989+
// Clear log
990+
log.length = 0;
991+
992+
// Commit
993+
batch.commit();
994+
dispatchClickEvent(ref.current);
995+
expect(log).toEqual([{counter: 1}]);
996+
});
937997
});

packages/react-native-renderer/src/ReactFabricHostConfig.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,6 @@ export function mountResponderInstance(
449449
props: Object,
450450
state: Object,
451451
instance: Instance,
452-
rootContainerInstance: Container,
453452
) {
454453
if (enableFlareAPI) {
455454
const {rootEventTypes} = responder;

packages/react-native-renderer/src/ReactNativeHostConfig.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,6 @@ export function mountResponderInstance(
501501
props: Object,
502502
state: Object,
503503
instance: Instance,
504-
rootContainerInstance: Container,
505504
) {
506505
throw new Error('Not yet implemented.');
507506
}

packages/react-reconciler/src/ReactFiberCommitWork.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ import {
118118
} from './ReactHookEffectTags';
119119
import {didWarnAboutReassigningProps} from './ReactFiberBeginWork';
120120
import {runWithPriority, NormalPriority} from './SchedulerWithReactIntegration';
121+
import {updateEventListeners} from './ReactFiberEvents';
121122

122123
let didWarnAboutUndefinedSnapshotBeforeUpdate: Set<mixed> | null = null;
123124
if (__DEV__) {
@@ -1331,6 +1332,13 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
13311332
finishedWork,
13321333
);
13331334
}
1335+
if (enableFlareAPI) {
1336+
const prevListeners = oldProps.listeners;
1337+
const nextListeners = newProps.listeners;
1338+
if (prevListeners !== nextListeners) {
1339+
updateEventListeners(nextListeners, instance, finishedWork);
1340+
}
1341+
}
13341342
}
13351343
return;
13361344
}

0 commit comments

Comments
 (0)