Skip to content

Commit 3ae668f

Browse files
committed
Fix event listener untracking
1 parent e08683f commit 3ae668f

File tree

2 files changed

+84
-4
lines changed

2 files changed

+84
-4
lines changed

packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1619,8 +1619,7 @@ FragmentInstancePseudoElement.prototype.removeEventListener = function (
16191619
listener: EventListener,
16201620
optionsOrUseCapture?: EventListenerOptionsOrUseCapture,
16211621
): void {
1622-
const listeners = this._eventListeners;
1623-
if (listeners === undefined || listeners.length === 0) {
1622+
if (this._eventListeners === undefined || this._eventListeners.length === 0) {
16241623
return;
16251624
}
16261625
traverseFragmentInstanceChildren(
@@ -1633,12 +1632,12 @@ FragmentInstancePseudoElement.prototype.removeEventListener = function (
16331632
optionsOrUseCapture,
16341633
),
16351634
);
1636-
const index = indexOfEventListener(listeners, {
1635+
const index = indexOfEventListener(this._eventListeners, {
16371636
type,
16381637
listener,
16391638
optionsOrUseCapture,
16401639
});
1641-
this._eventListeners = listeners.splice(index, 1);
1640+
this._eventListeners.splice(index, 1);
16421641
};
16431642
function removeEventListenerFromChild(
16441643
type: string,

packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,87 @@ describe('FragmentRefs', () => {
412412
expect(logs).toEqual(['Host A', 'Host B']);
413413
});
414414

415+
// @gate enableFragmentRefs
416+
it('allows adding and cleaning up listeners in effects', async () => {
417+
const root = ReactDOMClient.createRoot(container);
418+
419+
let logs = [];
420+
function logClick(e) {
421+
logs.push(e.currentTarget.id);
422+
}
423+
424+
let rerender;
425+
let removeEventListeners;
426+
427+
function Test() {
428+
const fragmentRef = React.useRef(null);
429+
const [_, setState] = React.useState(0);
430+
rerender = () => {
431+
setState(p => p + 1);
432+
};
433+
removeEventListeners = () => {
434+
fragmentRef.current.removeEventListener('click', logClick);
435+
};
436+
React.useEffect(() => {
437+
fragmentRef.current.addEventListener('click', logClick);
438+
439+
return removeEventListeners;
440+
});
441+
442+
return (
443+
<Fragment ref={fragmentRef}>
444+
<div id="child-a" />
445+
</Fragment>
446+
);
447+
}
448+
449+
// The event listener was applied
450+
await act(() => root.render(<Test />));
451+
expect(logs).toEqual([]);
452+
document.querySelector('#child-a').click();
453+
expect(logs).toEqual(['child-a']);
454+
455+
// The event listener can be removed and re-added
456+
logs = [];
457+
await act(rerender);
458+
document.querySelector('#child-a').click();
459+
expect(logs).toEqual(['child-a']);
460+
461+
// The event listener
462+
// Split into two tests
463+
// 1. Removed event listeners are not added to new children
464+
// 2. It can remove and reapply event listeners in effects
465+
});
466+
467+
it('does not apply removed event listeners to new children', async () => {
468+
const root = ReactDOMClient.createRoot(container);
469+
const fragmentRef = React.createRef(null);
470+
function Test() {
471+
return (
472+
<Fragment ref={fragmentRef}>
473+
<div id="child-a" />
474+
</Fragment>
475+
);
476+
}
477+
478+
let logs = [];
479+
function logClick(e) {
480+
logs.push(e.currentTarget.id);
481+
}
482+
await act(() => {
483+
root.render(<Test />);
484+
});
485+
fragmentRef.current.addEventListener('click', logClick);
486+
const childA = document.querySelector('#child-a');
487+
childA.click();
488+
expect(logs).toEqual(['child-a']);
489+
490+
logs = [];
491+
fragmentRef.current.removeEventListener('click', logClick);
492+
childA.click();
493+
expect(logs).toEqual([]);
494+
});
495+
415496
describe('with activity', () => {
416497
// @gate enableFragmentRefs && enableActivity
417498
it('does not apply event listeners to hidden trees', async () => {

0 commit comments

Comments
 (0)