Skip to content

Commit 71583c7

Browse files
committed
Accept multiple observers per fragment
1 parent 30b2bc9 commit 71583c7

File tree

4 files changed

+14
-44
lines changed

4 files changed

+14
-44
lines changed

fixtures/dom/src/components/fixtures/fragment-refs/IntersectionObserverCase.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export default function IntersectionObserverCase() {
5454
const lastFragmentRefValue = fragmentRef.current;
5555
return () => {
5656
lastFragmentRefValue.unobserveUsing(observerRef.current);
57+
observerRef.current = null;
5758
};
5859
}, []);
5960

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

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2186,7 +2186,7 @@ type StoredEventListener = {
21862186
export type FragmentInstanceType = {
21872187
_fragmentFiber: Fiber,
21882188
_eventListeners: null | Array<StoredEventListener>,
2189-
_observer: null | IntersectionObserver | ResizeObserver,
2189+
_observers: null | Set<IntersectionObserver | ResizeObserver>,
21902190
addEventListener(
21912191
type: string,
21922192
listener: EventListener,
@@ -2205,7 +2205,7 @@ export type FragmentInstanceType = {
22052205
function FragmentInstance(this: FragmentInstanceType, fragmentFiber: Fiber) {
22062206
this._fragmentFiber = fragmentFiber;
22072207
this._eventListeners = null;
2208-
this._observer = null;
2208+
this._observers = null;
22092209
}
22102210
// $FlowFixMe[prop-missing]
22112211
FragmentInstance.prototype.addEventListener = function (
@@ -2293,14 +2293,10 @@ FragmentInstance.prototype.observeUsing = function (
22932293
this: FragmentInstanceType,
22942294
observer: IntersectionObserver | ResizeObserver,
22952295
): void {
2296-
if (this._observer !== null && this._observer !== observer) {
2297-
throw new Error(
2298-
'You are attaching an observer to a fragment instance that already has one. Fragment instances ' +
2299-
'can only have one observer. Use multiple fragment instances or first call unobserveUsing() to ' +
2300-
'remove the previous observer.',
2301-
);
2296+
if (this._observers === null) {
2297+
this._observers = new Set();
23022298
}
2303-
this._observer = observer;
2299+
this._observers.add(observer);
23042300
traverseFragmentInstanceChildren(
23052301
this,
23062302
this._fragmentFiber.child,
@@ -2320,21 +2316,21 @@ FragmentInstance.prototype.unobserveUsing = function (
23202316
this: FragmentInstanceType,
23212317
observer: IntersectionObserver | ResizeObserver,
23222318
): void {
2323-
if (this._observer === null || this._observer !== observer) {
2319+
if (this._observers === null || !this._observers.has(observer)) {
23242320
if (__DEV__) {
23252321
console.error(
23262322
'You are calling unobserveUsing() with an observer that is not being observed with this fragment ' +
23272323
'instance. First attach the observer with observeUsing()',
23282324
);
23292325
}
23302326
} else {
2327+
this._observers.delete(observer);
23312328
traverseFragmentInstanceChildren(
23322329
this,
23332330
this._fragmentFiber.child,
23342331
unobserveChild,
23352332
observer,
23362333
);
2337-
this._observer = null;
23382334
}
23392335
};
23402336
function unobserveChild(
@@ -2403,8 +2399,10 @@ export function commitNewChildToFragmentInstance(
24032399
childElement.addEventListener(type, listener, optionsOrUseCapture);
24042400
}
24052401
}
2406-
if (fragmentInstance._observer !== null) {
2407-
fragmentInstance._observer.observe(childElement);
2402+
if (fragmentInstance._observers !== null) {
2403+
fragmentInstance._observers.forEach(observer => {
2404+
observer.observe(childElement);
2405+
});
24082406
}
24092407
}
24102408

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

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -693,34 +693,6 @@ describe('FragmentRefs', () => {
693693
expect(logs).toEqual([]);
694694
});
695695

696-
// @gate enableFragmentRefs
697-
it('errors when observeUsing() is called with multiple observers', async () => {
698-
const fragmentRef = React.createRef();
699-
const observer = new IntersectionObserver(() => {});
700-
const observer2 = new IntersectionObserver(() => {});
701-
function Test() {
702-
return (
703-
<React.Fragment ref={fragmentRef}>
704-
<div />
705-
</React.Fragment>
706-
);
707-
}
708-
709-
const root = ReactDOMClient.createRoot(container);
710-
await act(() => root.render(<Test />));
711-
fragmentRef.current.observeUsing(observer);
712-
// Same observer doesn't throw
713-
fragmentRef.current.observeUsing(observer);
714-
// Different observer throws
715-
expect(() => {
716-
fragmentRef.current.observeUsing(observer2);
717-
}).toThrowError(
718-
'You are attaching an observer to a fragment instance that already has one. Fragment instances ' +
719-
'can only have one observer. Use multiple fragment instances or first call unobserveUsing() to ' +
720-
'remove the previous observer.',
721-
);
722-
});
723-
724696
// @gate enableFragmentRefs
725697
it('warns when unobserveUsing() is called with an observer that was not observed', async () => {
726698
const fragmentRef = React.createRef();

scripts/error-codes/codes.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,5 @@
537537
"549": "Cannot start a gesture with a disconnected AnimationTimeline.",
538538
"550": "useSwipeTransition is not yet supported in react-art.",
539539
"551": "useSwipeTransition is not yet supported in React Native.",
540-
"552": "Cannot use a useSwipeTransition() in a detached root.",
541-
"553": "You are attaching an observer to a fragment instance that already has one. Fragment instances can only have one observer. Use multiple fragment instances or first call unobserveUsing() to remove the previous observer."
542-
}
540+
"552": "Cannot use a useSwipeTransition() in a detached root."
541+
}

0 commit comments

Comments
 (0)