@@ -17,121 +17,85 @@ type Props<T extends Route> = SceneRendererProps &
17
17
style ?: StyleProp < ViewStyle > ;
18
18
} ;
19
19
20
- type State = {
21
- loading : boolean ;
22
- } ;
23
-
24
- export default class SceneView < T extends Route > extends React . Component <
25
- Props < T > ,
26
- State
27
- > {
28
- static getDerivedStateFromProps ( props : Props < Route > , state : State ) {
29
- if (
30
- state . loading &&
31
- Math . abs ( props . navigationState . index - props . index ) <=
32
- props . lazyPreloadDistance
33
- ) {
34
- // Always render the route when it becomes focused
35
- return { loading : false } ;
36
- }
37
-
38
- return null ;
20
+ export default function SceneView < T extends Route > ( {
21
+ children,
22
+ navigationState,
23
+ lazy,
24
+ layout,
25
+ index,
26
+ lazyPreloadDistance,
27
+ addEnterListener,
28
+ style,
29
+ } : Props < T > ) {
30
+ const [ isLoading , setIsLoading ] = React . useState (
31
+ Math . abs ( navigationState . index - index ) > lazyPreloadDistance
32
+ ) ;
33
+
34
+ if (
35
+ isLoading &&
36
+ Math . abs ( navigationState . index - index ) <= lazyPreloadDistance
37
+ ) {
38
+ // Always render the route when it becomes focused
39
+ setIsLoading ( false ) ;
39
40
}
40
41
41
- state = {
42
- loading :
43
- Math . abs ( this . props . navigationState . index - this . props . index ) >
44
- this . props . lazyPreloadDistance ,
45
- } ;
42
+ React . useEffect ( ( ) => {
43
+ const handleEnter = ( value : number ) => {
44
+ // If we're entering the current route, we need to load it
45
+ if ( value === index ) {
46
+ setIsLoading ( ( prevState ) => {
47
+ if ( prevState ) {
48
+ return false ;
49
+ }
50
+ return prevState ;
51
+ } ) ;
52
+ }
53
+ } ;
54
+
55
+ let unsubscribe : ( ( ) => void ) | undefined ;
56
+ let timer : NodeJS . Timeout ;
46
57
47
- componentDidMount ( ) {
48
- if ( this . props . lazy ) {
58
+ if ( lazy && isLoading ) {
49
59
// If lazy mode is enabled, listen to when we enter screens
50
- this . unsubscribe = this . props . addEnterListener ( this . handleEnter ) ;
51
- } else if ( this . state . loading ) {
60
+ unsubscribe = addEnterListener ( handleEnter ) ;
61
+ } else if ( isLoading ) {
52
62
// If lazy mode is not enabled, render the scene with a delay if not loaded already
53
63
// This improves the initial startup time as the scene is no longer blocking
54
- this . timerHandler = setTimeout (
55
- ( ) => this . setState ( { loading : false } ) ,
56
- 0
57
- ) ;
64
+ timer = setTimeout ( ( ) => setIsLoading ( false ) , 0 ) ;
58
65
}
59
- }
60
66
61
- componentDidUpdate ( prevProps : Props < T > , prevState : State ) {
62
- if (
63
- this . props . lazy !== prevProps . lazy ||
64
- this . state . loading !== prevState . loading
65
- ) {
66
- // We only need the listener if the tab hasn't loaded yet and lazy is enabled
67
- if ( this . props . lazy && this . state . loading ) {
68
- this . unsubscribe ?.( ) ;
69
- this . unsubscribe = this . props . addEnterListener ( this . handleEnter ) ;
70
- } else {
71
- this . unsubscribe ?.( ) ;
67
+ return ( ) => {
68
+ unsubscribe ?.( ) ;
69
+ clearTimeout ( timer ) ;
70
+ } ;
71
+ } , [ addEnterListener , index , isLoading , lazy ] ) ;
72
+
73
+ const focused = navigationState . index === index ;
74
+
75
+ return (
76
+ < View
77
+ accessibilityElementsHidden = { ! focused }
78
+ importantForAccessibility = { focused ? 'auto' : 'no-hide-descendants' }
79
+ style = { [
80
+ styles . route ,
81
+ // If we don't have the layout yet, make the focused screen fill the container
82
+ // This avoids delay before we are able to render pages side by side
83
+ layout . width
84
+ ? { width : layout . width }
85
+ : focused
86
+ ? StyleSheet . absoluteFill
87
+ : null ,
88
+ style ,
89
+ ] }
90
+ >
91
+ {
92
+ // Only render the route only if it's either focused or layout is available
93
+ // When layout is not available, we must not render unfocused routes
94
+ // so that the focused route can fill the screen
95
+ focused || layout . width ? children ( { loading : isLoading } ) : null
72
96
}
73
- }
74
- }
75
-
76
- componentWillUnmount ( ) {
77
- this . unsubscribe ?.( ) ;
78
-
79
- if ( this . timerHandler ) {
80
- clearTimeout ( this . timerHandler ) ;
81
- this . timerHandler = undefined ;
82
- }
83
- }
84
-
85
- private timerHandler : NodeJS . Timeout | undefined ;
86
-
87
- private unsubscribe : ( ( ) => void ) | null = null ;
88
-
89
- private handleEnter = ( value : number ) => {
90
- const { index } = this . props ;
91
-
92
- // If we're entering the current route, we need to load it
93
- if ( value === index ) {
94
- this . setState ( ( prevState ) => {
95
- if ( prevState . loading ) {
96
- return { loading : false } ;
97
- }
98
-
99
- return null ;
100
- } ) ;
101
- }
102
- } ;
103
-
104
- render ( ) {
105
- const { navigationState, index, layout, style } = this . props ;
106
- const { loading } = this . state ;
107
-
108
- const focused = navigationState . index === index ;
109
-
110
- return (
111
- < View
112
- accessibilityElementsHidden = { ! focused }
113
- importantForAccessibility = { focused ? 'auto' : 'no-hide-descendants' }
114
- style = { [
115
- styles . route ,
116
- // If we don't have the layout yet, make the focused screen fill the container
117
- // This avoids delay before we are able to render pages side by side
118
- layout . width
119
- ? { width : layout . width }
120
- : focused
121
- ? StyleSheet . absoluteFill
122
- : null ,
123
- style ,
124
- ] }
125
- >
126
- {
127
- // Only render the route only if it's either focused or layout is available
128
- // When layout is not available, we must not render unfocused routes
129
- // so that the focused route can fill the screen
130
- focused || layout . width ? this . props . children ( { loading } ) : null
131
- }
132
- </ View >
133
- ) ;
134
- }
97
+ </ View >
98
+ ) ;
135
99
}
136
100
137
101
const styles = StyleSheet . create ( {
0 commit comments