Skip to content

Commit 74b5004

Browse files
committed
observe latest node when ref is used on multiple elements
1 parent dcdd47c commit 74b5004

4 files changed

Lines changed: 47 additions & 39 deletions

File tree

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/react-intersection-observer-hook/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
<!-- ALL-CONTRIBUTORS-BADGE:END -->
1212

13-
This is a easy to use React hook package for using [Insersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) declaratively. By using this hook, you can easily track if a component is visible or not, create lazy loading images, trigger animations on entering or leaving the viewport, implement infinite scroll etc.
13+
This is an easy to use React hook package for using [Insersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) declaratively. By using this hook, you can easily track if a component is visible or not, create lazy loading images, trigger animations on entering or leaving the viewport, implement infinite scroll etc.
1414

1515
**Live demo is [here](https://onderonur.github.io/react-intersection-observer-hook).**
1616

@@ -107,6 +107,10 @@ function Example() {
107107
}
108108
```
109109

110+
> [!NOTE]
111+
> The returned `ref` from `useIntersectionObserver` and `useTrackVisibility` can be attached to **only** one element at a time. If you assign it to multiple elements, only the most recently assigned element will be observed.
112+
> Currently, there is no support for observing multiple elements by using the same `ref`. Documentation will be updated when this is implemented.
113+
110114
You can find more usage examples in the `demo` app in this repository.
111115

112116
## Arguments

packages/react-intersection-observer-hook/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-intersection-observer-hook",
3-
"version": "4.0.0",
3+
"version": "4.0.1",
44
"description": "React hook to use IntersectionObserver declaratively.",
55
"keywords": [
66
"react",

packages/react-intersection-observer-hook/src/use-intersection-observer.ts

Lines changed: 40 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useCallback, useRef, useState } from 'react';
22
import { type Omit } from './types';
3-
import { createObserverCache, type CachedIntersectionObserver } from './utils';
3+
import { type CachedIntersectionObserver, createObserverCache } from './utils';
44

55
const DEFAULT_ROOT_MARGIN = '0px';
66
const DEFAULT_THRESHOLD = [0];
@@ -34,6 +34,11 @@ export type IntersectionObserverHookResult = [
3434
},
3535
];
3636

37+
type ReinitializeObserverCallback = (args: {
38+
newNode: IntersectionObserverHookRefCallbackNode;
39+
newRoot: IntersectionObserverHookRootRefCallbackNode;
40+
}) => void;
41+
3742
const observerCache = createObserverCache();
3843

3944
// For more info:
@@ -51,65 +56,64 @@ function useIntersectionObserver(
5156

5257
const [entry, setEntry] = useState<IntersectionObserverEntry>();
5358

54-
const reinitializeObserver = useCallback(() => {
55-
function cleanupObserver() {
56-
const observer = observerRef.current;
57-
const node = nodeRef.current;
58-
59-
if (node) {
60-
observer?.unobserve(node);
61-
setEntry(undefined);
62-
}
59+
const reinitializeObserver = useCallback<ReinitializeObserverCallback>(
60+
({ newNode, newRoot }) => {
61+
function cleanupObserver() {
62+
const observer = observerRef.current;
63+
const node = nodeRef.current;
6364

64-
observerRef.current = null;
65-
}
65+
if (node) {
66+
observer?.unobserve(node);
67+
setEntry(undefined);
68+
}
6669

67-
function initializeObserver() {
68-
const node = nodeRef.current;
70+
observerRef.current = null;
71+
}
6972

70-
if (!node) return;
73+
function initializeObserver() {
74+
if (!newNode) return;
7175

72-
const observer = observerCache.getObserver({
73-
root: rootRef.current,
74-
rootMargin,
75-
threshold,
76-
});
76+
const newObserver = observerCache.getObserver({
77+
root: newRoot,
78+
rootMargin,
79+
threshold,
80+
});
7781

78-
observer.observe(node, (observedEntry) => {
79-
setEntry(observedEntry);
80-
});
82+
newObserver.observe(newNode, (observedEntry) => {
83+
setEntry(observedEntry);
84+
});
8185

82-
observerRef.current = observer;
83-
}
86+
observerRef.current = newObserver;
87+
nodeRef.current = newNode;
88+
rootRef.current = newRoot;
89+
}
8490

85-
cleanupObserver();
86-
initializeObserver();
87-
}, [rootMargin, threshold]);
91+
cleanupObserver();
92+
initializeObserver();
93+
},
94+
[rootMargin, threshold],
95+
);
8896

8997
// React will call the ref callback with the DOM element when the component mounts,
9098
// and call its cleanup function when it unmounts.
9199
// So, we don't need an useEffect etc to unobserve nodes.
92100
const refCallback = useCallback<IntersectionObserverHookRefCallback>(
93101
(node) => {
94-
nodeRef.current = node;
95-
reinitializeObserver();
102+
reinitializeObserver({ newNode: node, newRoot: rootRef.current });
96103

97104
return () => {
98-
nodeRef.current = null;
99-
reinitializeObserver();
105+
reinitializeObserver({ newNode: null, newRoot: rootRef.current });
100106
};
101107
},
102108
[reinitializeObserver],
103109
);
104110

105111
const rootRefCallback = useCallback<IntersectionObserverHookRootRefCallback>(
106112
(rootNode) => {
107-
rootRef.current = rootNode;
108-
reinitializeObserver();
113+
reinitializeObserver({ newNode: nodeRef.current, newRoot: rootNode });
109114

110115
return () => {
111-
rootRef.current = null;
112-
reinitializeObserver();
116+
reinitializeObserver({ newNode: nodeRef.current, newRoot: null });
113117
};
114118
},
115119
[reinitializeObserver],

0 commit comments

Comments
 (0)