react-native-tab-view 3.2.1 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/lib/commonjs/PagerViewAdapter.js +2 -1
  2. package/lib/commonjs/PagerViewAdapter.js.map +1 -1
  3. package/lib/commonjs/PanResponderAdapter.js +2 -1
  4. package/lib/commonjs/PanResponderAdapter.js.map +1 -1
  5. package/lib/commonjs/SceneMap.js +9 -12
  6. package/lib/commonjs/SceneMap.js.map +1 -1
  7. package/lib/commonjs/SceneView.js +54 -101
  8. package/lib/commonjs/SceneView.js.map +1 -1
  9. package/lib/commonjs/TabBar.js +327 -327
  10. package/lib/commonjs/TabBar.js.map +1 -1
  11. package/lib/commonjs/TabBarIndicator.js +81 -99
  12. package/lib/commonjs/TabBarIndicator.js.map +1 -1
  13. package/lib/commonjs/TabBarItem.js +184 -161
  14. package/lib/commonjs/TabBarItem.js.map +1 -1
  15. package/lib/module/PagerViewAdapter.js +2 -1
  16. package/lib/module/PagerViewAdapter.js.map +1 -1
  17. package/lib/module/PanResponderAdapter.js +2 -1
  18. package/lib/module/PanResponderAdapter.js.map +1 -1
  19. package/lib/module/SceneMap.js +9 -14
  20. package/lib/module/SceneMap.js.map +1 -1
  21. package/lib/module/SceneView.js +53 -98
  22. package/lib/module/SceneView.js.map +1 -1
  23. package/lib/module/TabBar.js +323 -323
  24. package/lib/module/TabBar.js.map +1 -1
  25. package/lib/module/TabBarIndicator.js +74 -92
  26. package/lib/module/TabBarIndicator.js.map +1 -1
  27. package/lib/module/TabBarItem.js +178 -154
  28. package/lib/module/TabBarItem.js.map +1 -1
  29. package/lib/typescript/SceneMap.d.ts +5 -3
  30. package/lib/typescript/SceneView.d.ts +1 -18
  31. package/lib/typescript/TabBar.d.ts +7 -38
  32. package/lib/typescript/TabBarIndicator.d.ts +2 -10
  33. package/lib/typescript/TabBarItem.d.ts +3 -5
  34. package/package.json +4 -1
  35. package/src/PagerViewAdapter.tsx +6 -1
  36. package/src/PanResponderAdapter.tsx +6 -3
  37. package/src/SceneMap.tsx +11 -7
  38. package/src/SceneView.tsx +70 -106
  39. package/src/TabBar.tsx +451 -391
  40. package/src/TabBarIndicator.tsx +108 -114
  41. package/src/TabBarItem.tsx +214 -185
package/src/SceneMap.tsx CHANGED
@@ -1,19 +1,23 @@
1
1
  import * as React from 'react';
2
2
  import type { SceneRendererProps } from './types';
3
3
 
4
- class SceneComponent<
5
- T extends { component: React.ComponentType<any> }
6
- > extends React.PureComponent<T> {
7
- render() {
8
- const { component, ...rest } = this.props;
4
+ type SceneProps = {
5
+ route: any;
6
+ } & Omit<SceneRendererProps, 'layout'>;
7
+
8
+ const SceneComponent = React.memo(
9
+ <T extends { component: React.ComponentType<any> } & SceneProps>({
10
+ component,
11
+ ...rest
12
+ }: T) => {
9
13
  return React.createElement(component, rest);
10
14
  }
11
- }
15
+ );
12
16
 
13
17
  export default function SceneMap<T extends any>(scenes: {
14
18
  [key: string]: React.ComponentType<T>;
15
19
  }) {
16
- return ({ route, jumpTo, position }: SceneRendererProps & { route: any }) => (
20
+ return ({ route, jumpTo, position }: SceneProps) => (
17
21
  <SceneComponent
18
22
  key={route.key}
19
23
  component={scenes[route.key]}
package/src/SceneView.tsx CHANGED
@@ -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({