@@ -5,8 +5,10 @@ import { observe } from "./observe";
55export const useIsomorphicLayoutEffect =
66 typeof window === "undefined" ? React . useEffect : React . useLayoutEffect ;
77
8- const useSyncEffect =
9- Reflect . get ( React , "useInsertionEffect" ) || useIsomorphicLayoutEffect ;
8+ const useInsertionEffect = Reflect . get ( React , "useInsertionEffect" ) as
9+ | typeof React . useEffect
10+ | undefined ;
11+ const useSyncEffect = useInsertionEffect ?? React . useEffect ;
1012
1113export function supportsRefCleanup ( version : string | undefined ) {
1214 return version ?. startsWith ( "19." ) || false ;
@@ -26,6 +28,16 @@ type ObserverRefOptions = IntersectionObserverInitWithOptions & {
2628 triggerOnce ?: boolean ;
2729} ;
2830
31+ type ObserverRefCallback < TElement extends Element > = (
32+ element : TElement | undefined | null ,
33+ ) => ( ( ) => void ) | undefined ;
34+
35+ type ObserverState < TElement extends Element > = {
36+ node : TElement | null ;
37+ stop : ( ( ) => void ) | undefined ;
38+ owner : ObserverRefCallback < TElement > | null ;
39+ } ;
40+
2941export function useIntersectionObserverRef < TElement extends Element > (
3042 onIntersectionChange : ObserverCallback < TElement > ,
3143 {
@@ -41,13 +53,17 @@ export function useIntersectionObserverRef<TElement extends Element>(
4153 } : ObserverRefOptions ,
4254) {
4355 const onIntersectionChangeRef = React . useRef ( onIntersectionChange ) ;
44- const observerStateRef = React . useRef <
45- [
46- TElement | null ,
47- ( ( ) => void ) | undefined ,
48- ( ( element ?: TElement | null ) => void ) | null ,
49- ]
50- > ( [ null , undefined , null ] ) ;
56+ const observerStateRef = React . useRef < ObserverState < TElement > > ( {
57+ node : null ,
58+ stop : undefined ,
59+ owner : null ,
60+ } ) ;
61+
62+ // React 17 has no effect that runs before callback refs attach. Keep its
63+ // synchronous fallback behavior compatible by publishing during render.
64+ if ( ! useInsertionEffect ) {
65+ onIntersectionChangeRef . current = onIntersectionChange ;
66+ }
5167
5268 useSyncEffect ( ( ) => {
5369 onIntersectionChangeRef . current = onIntersectionChange ;
@@ -58,41 +74,41 @@ export function useIntersectionObserverRef<TElement extends Element>(
5874 function setRef ( element : TElement | undefined | null ) {
5975 const observerState = observerStateRef . current ;
6076
61- if ( ! element && observerState [ 2 ] !== setRef ) {
77+ if ( ! element && observerState . owner !== setRef ) {
6278 return ;
6379 }
6480
65- if ( element === observerState [ 0 ] ) {
66- observerState [ 2 ] = setRef ;
67- return canUseRefCleanup ? observerState [ 1 ] : undefined ;
81+ if ( element === observerState . node ) {
82+ observerState . owner = setRef ;
83+ return canUseRefCleanup ? observerState . stop : undefined ;
6884 }
6985
70- const cleanup = observerState [ 1 ] ;
71- observerState [ 1 ] = undefined ;
86+ const cleanup = observerState . stop ;
87+ observerState . stop = undefined ;
7288 cleanup ?.( ) ;
7389
7490 if ( ! element || skip ) {
75- observerState [ 0 ] = null ;
76- observerState [ 2 ] = element ? setRef : null ;
91+ observerState . node = null ;
92+ observerState . owner = element ? setRef : null ;
7793 return ;
7894 }
7995
80- observerState [ 0 ] = element ;
81- observerState [ 2 ] = setRef ;
96+ observerState . node = element ;
97+ observerState . owner = setRef ;
8298
8399 let destroyObserver : ( ( ) => void ) | undefined ;
84100 let previousInView : boolean | undefined ;
85101
86102 function stopObserving ( ) {
87103 destroyObserver ?.( ) ;
88104
89- if ( observerState [ 1 ] === stopObserving ) {
90- observerState [ 0 ] = null ;
91- observerState [ 1 ] = undefined ;
105+ if ( observerState . stop === stopObserving ) {
106+ observerState . node = null ;
107+ observerState . stop = undefined ;
92108 }
93109 }
94110
95- observerState [ 1 ] = stopObserving ;
111+ observerState . stop = stopObserving ;
96112 destroyObserver = observe (
97113 element ,
98114 ( inView , entry ) => {
@@ -115,9 +131,9 @@ export function useIntersectionObserverRef<TElement extends Element>(
115131 fallbackInView ,
116132 ) ;
117133
118- if ( observerState [ 1 ] !== stopObserving ) destroyObserver ( ) ;
134+ if ( observerState . stop !== stopObserving ) destroyObserver ( ) ;
119135
120- return canUseRefCleanup ? observerState [ 1 ] : undefined ;
136+ return canUseRefCleanup ? observerState . stop : undefined ;
121137 } ,
122138 [
123139 Array . isArray ( threshold ) ? threshold . toString ( ) : threshold ,
0 commit comments