Skip to content
This repository was archived by the owner on Nov 27, 2022. It is now read-only.

Commit 1abaed6

Browse files
satya164osdnk
andcommitted
feat: migrate to ViewPager library
BREAKING CHANGE: This drops `react-native-gesture-handler` and `react-native-reanimated`. All custom logic using GestureHandler and Reanimated will need to be rewritten. Now that ViewPager is better maintained and cross-platform, it's time to migrate to it for native perf and also less maintenance cost. Co-authored-by: Michał Osadnik <[email protected]>
1 parent 5aec849 commit 1abaed6

35 files changed

+7370
-9341
lines changed

.github/ISSUE_TEMPLATE/bug_report.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ For Syntax Highlighting check this [link](https://help.github.com/en/articles/cr
4040
| expo |
4141
| react-native |
4242
| react-native-tab-view |
43-
| react-native-gesture-handler |
44-
| react-native-reanimated |
43+
| react-native-pager-view |
4544
| node |
4645
| npm or yarn |

.github/workflows/triage.yml

+3-18
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ jobs:
3434
body: "Hey! Thanks for opening the issue. Can you provide a [minimal repro](https://stackoverflow.com/help/minimal-reproducible-example) which demonstrates the issue? Posting a snippet of your code in the issue is useful, but it's not usually straightforward to run. A repro will help us debug the issue faster. Please try to keep the repro as small as possible.\n\nThe easiest way to provide a repro is on [snack.expo.io](https://snack.expo.io). If it's not possible to repro it on [snack.expo.io](https://snack.expo.io), then please provide the repro in a GitHub repository."
3535
})
3636
37-
react-native-reanimated:
37+
react-native-pager-view:
3838
runs-on: ubuntu-latest
39-
if: github.event.label.name == 'library:react-native-reanimated'
39+
if: github.event.label.name == 'library:react-native-pager-view'
4040
steps:
4141
- uses: actions/github-script@v2
4242
with:
@@ -46,20 +46,5 @@ jobs:
4646
issue_number: context.issue.number,
4747
owner: context.repo.owner,
4848
repo: context.repo.repo,
49-
body: "Hey! Thanks for opening the issue. The issue tracker is intended for only tracking bug reports in React Navigation. Seems you have an issue related to `react-native-reanimated` library. Please post your issue in [this repo](https://github.com/software-mansion/react-native-reanimated) so that it's notified to the maintainers of that library."
50-
})
51-
52-
react-native-gesture-handler:
53-
runs-on: ubuntu-latest
54-
if: github.event.label.name == 'library:react-native-gesture-handler'
55-
steps:
56-
- uses: actions/github-script@v2
57-
with:
58-
github-token: ${{secrets.GITHUB_TOKEN}}
59-
script: |
60-
github.issues.createComment({
61-
issue_number: context.issue.number,
62-
owner: context.repo.owner,
63-
repo: context.repo.repo,
64-
body: "Hey! Thanks for opening the issue. The issue tracker is intended for only tracking bug reports in React Navigation. Seems you have an issue related to `react-native-gesture-handler` library. Please post your issue in [this repo](https://github.com/software-mansion/react-native-gesture-handler) so that it's notified to the maintainers of that library."
49+
body: "Hey! Thanks for opening the issue. Seems that this issue issue related to `react-native-pager-view` library which is a dependency of `react-native-tab-view`. Can you also post your issue in [this repo](https://github.com/callstack/react-native-viewpager) so that it's notified to the maintainers of that library? This will help us fix the issue faster since it's upto the maintainers of that library to investigate it."
6550
})

.github/workflows/versions.yml

+1-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ jobs:
1313
required-packages: |
1414
react-native
1515
react-native-tab-view
16-
react-native-gesture-handler
17-
react-native-reanimated
16+
react-native-pager-view
1817
optional-packages: |
1918
expo

.yarnrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
yarn-path "scripts/bootstrap.js"

README.md

+13-189
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
[![Version][version-badge]][package]
55
[![MIT License][license-badge]][license]
66

7-
A cross-platform Tab View component for React Native.
7+
A cross-platform Tab View component for React Native. Implemented using [`react-native-pager-view`](https://github.com/callstack/react-native-view-pager) on Android & iOS, and [PanResponder](https://reactnative.dev/docs/panresponder) on Web.
88

99
- [Run the example app to see it in action](https://expo.io/@satya164/react-native-tab-view-demos).
1010
- Checkout the [example/](https://github.com/satya164/react-native-tab-view/tree/main/example) folder for source code.
@@ -30,67 +30,40 @@ Open a Terminal in the project root and run:
3030
yarn add react-native-tab-view
3131
```
3232

33-
Now we need to install [`react-native-gesture-handler`](https://github.com/kmagiera/react-native-gesture-handler) and [`react-native-reanimated`](https://github.com/kmagiera/react-native-reanimated).
33+
Now we need to install [`react-native-pager-view`](https://github.com/callstack/react-native-viewpager).
3434

3535
If you are using Expo, to ensure that you get the compatible versions of the libraries, run:
3636

3737
```sh
38-
expo install react-native-gesture-handler react-native-reanimated
38+
expo install react-native-pager-view
3939
```
4040

4141
If you are not using Expo, run the following:
4242

4343
```sh
44-
yarn add react-native-reanimated react-native-gesture-handler
44+
yarn add react-native-pager-view
4545
```
4646

47-
If you are using Expo, you are done. Otherwise, continue to the next steps.
48-
49-
Next, we need to link these libraries. The steps depends on your React Native version:
50-
51-
- **React Native 0.60 and higher**
52-
53-
On newer versions of React Native, [linking is automatic](https://github.com/react-native-community/cli/blob/main/docs/autolinking.md).
54-
55-
To complete the linking on iOS, make sure you have [Cocoapods](https://cocoapods.org/) installed. Then run:
56-
57-
```sh
58-
cd ios
59-
pod install
60-
cd ..
61-
```
62-
63-
- **React Native 0.59 and lower**
64-
65-
If you're on an older React Native version, you need to manually link the dependencies. To do that, run:
66-
67-
```sh
68-
react-native link react-native-reanimated
69-
react-native link react-native-gesture-handler
70-
```
71-
72-
**IMPORTANT:** There are additional steps required for `react-native-gesture-handler` on Android after linking (for all React Native versions). Check the [this guide](https://docs.swmansion.com/react-native-gesture-handler/docs/) to complete the installation.
73-
7447
We're done! Now you can build and run the app on your device/simulator.
7548

7649
## Quick Start
7750

7851
```js
7952
import * as React from 'react';
80-
import { View, StyleSheet, Dimensions } from 'react-native';
53+
import { View, useWindowDimensions } from 'react-native';
8154
import { TabView, SceneMap } from 'react-native-tab-view';
8255

8356
const FirstRoute = () => (
84-
<View style={[styles.scene, { backgroundColor: '#ff4081' }]} />
57+
<View style={{ flex: 1, backgroundColor: '#ff4081' }} />
8558
);
8659

8760
const SecondRoute = () => (
88-
<View style={[styles.scene, { backgroundColor: '#673ab7' }]} />
61+
<View style={{ flex: 1, backgroundColor: '#673ab7' }} />
8962
);
9063

91-
const initialLayout = { width: Dimensions.get('window').width };
92-
9364
export default function TabViewExample() {
65+
const layout = useWindowDimensions();
66+
9467
const [index, setIndex] = React.useState(0);
9568
const [routes] = React.useState([
9669
{ key: 'first', title: 'First' },
@@ -107,16 +80,10 @@ export default function TabViewExample() {
10780
navigationState={{ index, routes }}
10881
renderScene={renderScene}
10982
onIndexChange={setIndex}
110-
initialLayout={initialLayout}
83+
initialLayout={{ width: layout.width }}
11184
/>
11285
);
11386
}
114-
115-
const styles = StyleSheet.create({
116-
scene: {
117-
flex: 1,
118-
},
119-
});
12087
```
12188

12289
[Try this example on Snack](https://snack.expo.io/@satya164/react-native-tab-view-quick-start)
@@ -147,7 +114,7 @@ Basic usage look like this:
147114
/>
148115
```
149116

150-
#### Props
117+
#### TabView Props
151118

152119
##### `navigationState` (`required`)
153120

@@ -281,36 +248,6 @@ If this is not specified, the default tab bar is rendered. You pass this props t
281248
/>
282249
```
283250

284-
##### `renderPager`
285-
286-
Callback which returns a custom React Element to use as pager.
287-
288-
E.g. you can import `ScrollPager` from `react-native-tab-view`. It might deliver slightly better experience on iOS.
289-
290-
```js
291-
import { TabView, ScrollPager } from 'react-native-tab-view';
292-
// ...
293-
<TabView
294-
renderPager={props => <ScrollPager { ...props }/>}
295-
// ...
296-
/>
297-
```
298-
299-
Also, you can use `ViewPager`-based pager with [`React Native Tab View ViewPager Adapter
300-
`](https://github.com/software-mansion/react-native-tab-view-viewpager-adapter).
301-
302-
```js
303-
import { TabView } from 'react-native-tab-view';
304-
import ViewPagerAdapter from 'react-native-tab-view-viewpager-adapter';
305-
// ...
306-
<TabView
307-
renderPager={props => (
308-
<ViewPagerAdapter {...props} transition="curl" showPageIndicator />
309-
)}
310-
// ...
311-
/>
312-
```
313-
314251
##### `tabBarPosition`
315252

316253
Position of the tab bar in the tab view. Possible values are `'top'` and `'bottom'`. Defaults to `'top'`.
@@ -368,10 +305,6 @@ String indicating whether the keyboard gets dismissed in response to a drag gest
368305

369306
Boolean indicating whether to enable swipe gestures. Swipe gestures are enabled by default. Passing `false` will disable swipe gestures, but the user can still switch tabs by pressing the tab bar.
370307

371-
##### `swipeVelocityImpact`
372-
373-
Determines how relevant is a velocity while calculating next position while swiping. Defaults to `0.2`.
374-
375308
##### `onSwipeStart`
376309

377310
Callback which is called when the swipe gesture starts, i.e. the user touches the screen and moves it.
@@ -380,26 +313,6 @@ Callback which is called when the swipe gesture starts, i.e. the user touches th
380313

381314
Callback which is called when the swipe gesture ends, i.e. the user lifts their finger from the screen after the swipe gesture.
382315

383-
##### `timingConfig`
384-
385-
Configuration object for the timing animation which occurs when tapping on tabs. Supported properties are:
386-
387-
- `duration` (`number`)
388-
389-
##### `springConfig`
390-
391-
Configuration object for the spring animation which occurs after swiping. Supported properties are:
392-
393-
- `damping` (`number`)
394-
- `mass` (`number`)
395-
- `stiffness` (`number`)
396-
- `restSpeedThreshold` (`number`)
397-
- `restDisplacementThreshold` (`number`)
398-
399-
##### `springVelocityScale`
400-
401-
Number for determining how meaningful is gesture velocity for calculating initial velocity of spring animation. Defaults to `0`.
402-
403316
##### `initialLayout`
404317

405318
Object containing the initial height and width of the screens. Passing this will improve the initial rendering performance. For most apps, this is a good default:
@@ -411,21 +324,6 @@ Object containing the initial height and width of the screens. Passing this will
411324
/>
412325
```
413326

414-
##### `position`
415-
416-
Animated value to listen to the position updates. The passed position value will be kept in sync with the current position of the tabs. It's useful for accessing the animated value outside the tab view.
417-
418-
```js
419-
const [position] = useState(() => new Animated.Value(0));
420-
421-
return (
422-
<TabView
423-
position={position}
424-
...
425-
/>
426-
);
427-
```
428-
429327
##### `sceneContainerStyle`
430328

431329
Style to apply to the view wrapping each screen. You can pass this to override some default styles such as overflow clipping:
@@ -434,20 +332,6 @@ Style to apply to the view wrapping each screen. You can pass this to override s
434332

435333
Style to apply to the tab view container.
436334

437-
##### `gestureHandlerProps`
438-
439-
An object with props to be passed to underlying [`PanGestureHandler`](https://kmagiera.github.io/react-native-gesture-handler/docs/handler-pan.html#properties). For example:
440-
441-
```js
442-
<TabView
443-
gestureHandlerProps={{
444-
maxPointers: 1,
445-
waitFor: [someRef]
446-
}}
447-
...
448-
/>
449-
```
450-
451335
### `TabBar`
452336

453337
Material design themed tab bar. To customize the tab bar, you'd need to use the `renderTabBar` prop of `TabView` to render the `TabBar` and pass additional props.
@@ -474,7 +358,7 @@ return (
474358
);
475359
```
476360

477-
#### Props
361+
#### TabBar Props
478362

479363
##### `getLabelText`
480364

@@ -631,77 +515,17 @@ Style to apply to the inner container for tabs.
631515

632516
Style to apply to the tab bar container.
633517

634-
### `ScrollPager`
635-
Custom pager which can we used inside `renderPager` prop. It is based on ScrollView and might bring a slightly better experience on iOS.
636-
637-
#### Props
638-
It accepts the same set of props as default pager extended with one addition:
639-
640-
##### ovescroll
641-
When `true`, the scroll view bounces when it reaches the end of the content. The default value is `false`.
642-
643-
644518
## Using with other libraries
645519

646520
### [React Navigation](https://github.com/react-navigation/react-navigation)
647521

648522
If you want to integrate the tab view with React Navigation's navigation system, e.g. want to be able to navigate to a tab using `navigation.navigate` etc, you can use the following official integrations:
649523

650-
- [@react-navigation/material-top-tabs](https://github.com/react-navigation/react-navigation/tree/main/packages/material-top-tabs) for React Navigation 5
524+
- [@react-navigation/material-top-tabs](https://github.com/react-navigation/react-navigation/tree/main/packages/material-top-tabs) for React Navigation 5 & 6
651525
- [react-navigation-tabs](https://github.com/react-navigation/react-navigation-tabs) for React Navigation 4
652526

653527
Note that some functionalities are not available with the React Navigation 4 integration because of the limitations in React Navigation. For example, it's possible to dynamically change the rendered tabs.
654528

655-
### [React Native Navigation (Wix)](https://github.com/wix/react-native-navigation)
656-
657-
If you use React Native Navigation by Wix on Android, you need to wrap all your screens that uses `react-native-tab-view` with `gestureHandlerRootHOC` from `react-native-gesture-handler`. Refer [`react-native-gesture-handler`'s docs](https://kmagiera.github.io/react-native-gesture-handler/docs/getting-started.html#with-wix-react-native-navigation-https-githubcom-wix-react-native-navigation) for more details.
658-
659-
### [Mobx](https://mobx.js.org/)
660-
661-
Normally we recommend to use React's local state to manage the navigation state for the tabs. But if you need to use Mobx to manage the navigation state, there is a gotcha you need to be aware of.
662-
663-
Mobx relies on data being accessed in `render` to work properly. However, we don't use the `index` value inside `render` in the library, so Mobx fails to track any changes to the `index`. You might see that the tabs don't change on pressing on the tab bar if you have a state like this:
664-
665-
```js
666-
@observable navigationState = {
667-
index: 0,
668-
routes: [
669-
{ key: 'music', title: 'Music' },
670-
{ key: 'albums', title: 'Albums' },
671-
],
672-
};
673-
```
674-
675-
To workaround this, we need to make sure that `index` is accessed in `render`. We can refactor our state to something like this for it to work:
676-
677-
```js
678-
@observer
679-
class MyComponent extends React.Component {
680-
@observable index = 0;
681-
682-
@observable routes = [
683-
{ key: 'music', title: 'Music' },
684-
{ key: 'albums', title: 'Albums' },
685-
];
686-
687-
@action handleIndexChange = index => {
688-
this.index = index;
689-
};
690-
691-
render() {
692-
return (
693-
<TabView
694-
navigationState={{ index: this.index, routes: this.routes }}
695-
renderScene={({ route }) => {
696-
/* ... */
697-
}}
698-
onIndexChange={this.handleIndexChange}
699-
/>
700-
);
701-
}
702-
}
703-
```
704-
705529
## Optimization Tips
706530

707531
### Avoid unnecessary re-renders

example/.eslintrc.json

+1-3
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33

44
"settings": {
55
"import/core-modules": [
6-
"react-native-tab-view",
7-
"react-native-gesture-handler",
8-
"react-native-reanimated"
6+
"react-native-tab-view"
97
]
108
}
119
}

example/app.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"version": "1.0.0",
88
"primaryColor": "#2196f3",
99
"icon": "assets/icon.png",
10-
"loading": {
10+
"splash": {
1111
"icon": "assets/icon.png",
1212
"hideExponentText": false
1313
},

0 commit comments

Comments
 (0)