1
- import { useState , useEffect , useMemo , useCallback , useRef , Children , cloneElement , useContext } from 'react' ;
1
+ import {
2
+ useState ,
3
+ useEffect ,
4
+ useMemo ,
5
+ useCallback ,
6
+ useRef ,
7
+ Children ,
8
+ cloneElement ,
9
+ useContext ,
10
+ useLayoutEffect ,
11
+ } from 'react' ;
2
12
import { useInView } from 'react-intersection-observer' ;
3
13
4
14
import { isBrowser , getRootMargin , getProgressRootMargin } from './utils' ;
5
- import type { StepProps } from './types' ;
15
+ import type { StepProps , ScrollamaCallbackData } from './types' ;
6
16
import { ScrollamaProvide } from './provide' ;
7
17
8
18
export const Step : React . FC < StepProps > = ( {
@@ -20,6 +30,7 @@ export const Step: React.FC<StepProps> = ({
20
30
innerHeight = 0 ,
21
31
} = useContext ( ScrollamaProvide ) ;
22
32
33
+ const [ nodeOffsetHeight , setNodeOffsetHeight ] = useState ( 0 ) ;
23
34
const rootMargin = getRootMargin ( { offset } ) ;
24
35
const { ref : inViewRef , entry } = useInView ( {
25
36
rootMargin,
@@ -33,9 +44,10 @@ export const Step: React.FC<StepProps> = ({
33
44
const ref = useRef < HTMLElement | null > ( null ) ;
34
45
const [ isIntersecting , setIsIntersecting ] = useState ( false ) ;
35
46
47
+
36
48
const progressRootMargin = useMemo (
37
- ( ) => getProgressRootMargin ( { direction, offset, node : ref , innerHeight } ) ,
38
- [ direction , offset , ref , innerHeight ]
49
+ ( ) => getProgressRootMargin ( { direction, offset, nodeOffsetHeight , innerHeight } ) ,
50
+ [ direction , offset , nodeOffsetHeight , innerHeight ]
39
51
) ;
40
52
41
53
const { ref : scrollProgressRef , entry : scrollProgressEntry } = useInView ( {
@@ -55,7 +67,7 @@ export const Step: React.FC<StepProps> = ({
55
67
56
68
useEffect ( ( ) => {
57
69
if ( isIntersecting && scrollProgressEntry ) {
58
- const { height, top } = scrollProgressEntry . target . getBoundingClientRect ( ) ;
70
+ const { height, top } = scrollProgressEntry . boundingClientRect ;
59
71
const progress = Math . min ( 1 , Math . max ( 0 , ( window . innerHeight * offset - top ) / height ) ) ;
60
72
if ( onStepProgress ) {
61
73
onStepProgress ( {
@@ -70,22 +82,29 @@ export const Step: React.FC<StepProps> = ({
70
82
} , [ scrollProgressEntry ] ) ;
71
83
72
84
useEffect ( ( ) => {
73
- if ( entry && ! entry . isIntersecting && isIntersecting ) {
74
- setIsIntersecting ( false ) ;
75
- onStepExit ( { element : entry . target , data, entry, direction } ) ;
76
- handleSetLastScrollTop ( scrollTop )
77
- } else if ( entry && entry . isIntersecting && ! isIntersecting ) {
78
- setIsIntersecting ( true ) ;
79
- onStepEnter ( { element : entry . target , data, entry, direction } ) ;
80
- handleSetLastScrollTop ( scrollTop )
85
+ if ( entry ) {
86
+ const currentIntersectionState = entry . isIntersecting ;
87
+ if ( currentIntersectionState !== isIntersecting ) {
88
+ setIsIntersecting ( currentIntersectionState ) ;
89
+ const eventData : ScrollamaCallbackData < unknown > = { element : entry . target , data, entry, direction } ;
90
+ if ( currentIntersectionState ) {
91
+ onStepEnter ( eventData ) ;
92
+ } else {
93
+ onStepExit ( eventData ) ;
94
+ }
95
+ handleSetLastScrollTop ( scrollTop ) ;
96
+ }
81
97
}
82
98
} , [ entry ] ) ;
83
99
100
+ useLayoutEffect ( ( ) => {
101
+ if ( ref . current ) {
102
+ setNodeOffsetHeight ( ref . current . offsetHeight ) ;
103
+ }
104
+ } , [ ref . current ] ) ;
105
+
84
106
const childElement = Children . only ( children ) ;
85
- return cloneElement ( childElement , {
86
- ref : setRefs ,
87
- entry,
88
- } ) ;
107
+ return cloneElement ( childElement , { ref : setRefs } ) ;
89
108
} ;
90
109
91
110
0 commit comments