@@ -45,7 +45,7 @@ export function useInView({
4545 fallbackInView,
4646 onChange,
4747} : IntersectionOptions = { } ) : InViewHookResponse {
48- const ref = React . useRef < Element | null > ( null ) ;
48+ const [ ref , setRef ] = React . useState < Element | null > ( null ) ;
4949 const unobserve = React . useRef < Function > ( ) ;
5050 const callback = React . useRef < IntersectionOptions [ 'onChange' ] > ( ) ;
5151 const [ state , setState ] = React . useState < State > ( {
@@ -54,28 +54,21 @@ export function useInView({
5454 } ) ;
5555
5656 // Store the onChange callback in a `ref`, so we can access the latest instance
57- // inside the `useCallback `, but without triggering a rerender.
57+ // inside the `useEffect `, but without triggering a rerender.
5858 callback . current = onChange ;
5959
60- const setRef = React . useCallback (
61- ( node : Element ) => {
62- ref . current = node ;
60+ React . useEffect (
61+ ( ) => {
6362 // Ensure we have node ref, and that we shouldn't skip observing
64- if ( skip || ! node ) return ;
63+ if ( skip || ! ref ) return ;
6564
66- // Store a reference the current unobserve function, so we can destroy it later
67- const previousObserver = unobserve . current ;
68-
69- // Create a new IntersectionObserver, and store the `unobserve` function.
7065 unobserve . current = observe (
71- node ,
66+ ref ,
7267 ( inView , entry ) => {
7368 setState ( {
7469 inView,
7570 entry,
7671 } ) ;
77-
78- // Trigger any onChange callback function
7972 if ( callback . current ) callback . current ( inView , entry ) ;
8073
8174 if ( entry . isIntersecting && triggerOnce && unobserve . current ) {
@@ -96,18 +89,20 @@ export function useInView({
9689 fallbackInView ,
9790 ) ;
9891
99- if ( previousObserver ) {
100- // Was already observing a node - Make sure we destroy the previous observer.
101- // Do it after we create the new one, so the IntersectionObserver instance can be reused.
102- previousObserver ( ) ;
103- }
92+ return ( ) => {
93+ if ( unobserve . current ) {
94+ unobserve . current ( ) ;
95+ unobserve . current = undefined ;
96+ }
97+ } ;
10498 } ,
10599 // We break the rule here, because we aren't including the actual `threshold` variable
106100 // eslint-disable-next-line react-hooks/exhaustive-deps
107101 [
108102 // If the threshold is an array, convert it to a string, so it won't change between renders.
109103 // eslint-disable-next-line react-hooks/exhaustive-deps
110104 Array . isArray ( threshold ) ? threshold . toString ( ) : threshold ,
105+ ref ,
111106 root ,
112107 rootMargin ,
113108 triggerOnce ,
@@ -118,27 +113,18 @@ export function useInView({
118113 ] ,
119114 ) ;
120115
121- // We break the rule here, since we want to ensure we check the `ref` instances on every render.
122- // eslint-disable-next-line react-hooks/exhaustive-deps
116+ const entryTarget = state . entry ?. target ;
117+
123118 React . useEffect ( ( ) => {
124- if ( ! ref . current && state . entry ?. target && ! triggerOnce && ! skip ) {
119+ if ( ! ref && entryTarget && ! triggerOnce && ! skip ) {
125120 // If we don't have a node ref, then reset the state (unless the hook is set to only `triggerOnce` or `skip`)
126- // This ensures we correctly reflect the current state - If you aren't observing anything, then nothing is inView.
121+ // This ensures we correctly reflect the current state - If you aren't observing anything, then nothing is inView
127122 setState ( {
128123 inView : ! ! initialInView ,
129124 entry : undefined ,
130125 } ) ;
131126 }
132-
133- return ( ) => {
134- if ( ! ref . current && unobserve . current ) {
135- // We no longer have a valid ref. Destroy the observer
136- unobserve . current ( ) ;
137- unobserve . current = undefined ;
138- ref . current = null ;
139- }
140- } ;
141- } ) ;
127+ } , [ ref , entryTarget , triggerOnce , skip , initialInView ] ) ;
142128
143129 const result = [ setRef , state . inView , state . entry ] as InViewHookResponse ;
144130
0 commit comments