Skip to content

Commit 783dab8

Browse files
committed
fix lag and performance issues with TabController
1 parent 6c04eae commit 783dab8

File tree

3 files changed

+48
-50
lines changed

3 files changed

+48
-50
lines changed

demo/src/screens/componentScreens/TabControllerScreen/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ class TabControllerScreen extends Component {
128128
</TabController.TabBar>
129129
{this.renderTabPages()}
130130
</TabController>
131-
<View absB left margin-20 marginB-100>
131+
<View absB left margin-20 marginB-100 style={{zIndex: 1}}>
132132
<Button
133133
bg-grey20={!asCarousel}
134134
bg-green30={asCarousel}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
"prop-types": "^15.5.10",
5050
"react-native-animatable": "^1.1.0",
5151
"react-native-color": "0.0.10",
52-
"react-native-redash": "^11.2.1",
52+
"react-native-redash": "^12.0.3",
5353
"react-native-text-size": "4.0.0-rc.1",
5454
"semver": "^5.5.0",
5555
"url-parse": "^1.2.0"

src/components/tabController/index.js

Lines changed: 46 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import PropTypes from 'prop-types';
66
import _ from 'lodash';
77
import Reanimated, {Easing} from 'react-native-reanimated';
88
import {State} from 'react-native-gesture-handler';
9-
import {timing, fract} from 'react-native-redash';
9+
import {timing, fract, between} from 'react-native-redash';
1010
import {Constants} from '../../helpers';
1111
import TabBarContext from './TabBarContext';
1212
import TabBar from './TabBar';
@@ -16,12 +16,16 @@ import PageCarousel from './PageCarousel';
1616

1717
const {
1818
and,
19+
abs,
1920
cond,
2021
call,
2122
Code,
2223
Clock,
2324
clockRunning,
25+
diff,
2426
eq,
27+
floor,
28+
lessThan,
2529
neq,
2630
not,
2731
set,
@@ -52,10 +56,6 @@ class TabController extends Component {
5256
* callback for when index has change (will not be called on ignored items)
5357
*/
5458
onChangeIndex: PropTypes.func,
55-
// /**
56-
// * callback for when tab selected
57-
// */
58-
// onTabSelected: PropTypes.func,
5959
/**
6060
* When using TabController.PageCarousel this should be turned on
6161
*/
@@ -67,36 +67,22 @@ class TabController extends Component {
6767
activeOpacity: 0.2
6868
};
6969

70-
state = {
71-
selectedIndex: this.props.selectedIndex,
72-
itemStates: []
73-
};
74-
75-
_targetPage = new Value(this.props.selectedIndex);
76-
_currentPage = new Value(this.props.selectedIndex);
77-
_carouselOffset = new Value(this.props.selectedIndex * Math.round(Constants.screenWidth));
78-
79-
shouldComponentUpdate(nextProps) {
80-
if (nextProps.selectedIndex !== this.props.selectedIndex) {
81-
return false;
82-
}
83-
return true;
84-
}
85-
86-
getProviderContextValue = () => {
87-
const {itemStates, selectedIndex} = this.state;
88-
const {onChangeIndex, asCarousel} = this.props;
89-
return {
90-
selectedIndex,
91-
currentPage: this._currentPage,
92-
targetPage: this._targetPage,
93-
carouselOffset: this._carouselOffset,
94-
itemStates,
70+
constructor(props) {
71+
super(props);
72+
73+
this.state = {
74+
selectedIndex: this.props.selectedIndex,
75+
asCarousel: this.props.asCarousel,
76+
itemStates: [],
77+
// animated values
78+
targetPage: new Value(this.props.selectedIndex),
79+
currentPage: new Value(this.props.selectedIndex),
80+
carouselOffset: new Value(this.props.selectedIndex * Math.round(Constants.screenWidth)),
81+
// // callbacks
9582
registerTabItems: this.registerTabItems,
96-
onChangeIndex,
97-
asCarousel
83+
onChangeIndex: this.props.onChangeIndex
9884
};
99-
};
85+
}
10086

10187
registerTabItems = (tabItemsCount, ignoredItems) => {
10288
const itemStates = _.times(tabItemsCount, () => new Value(State.UNDETERMINED));
@@ -108,11 +94,13 @@ class TabController extends Component {
10894
};
10995

11096
renderCodeBlock = () => {
111-
const {itemStates, ignoredItems} = this.state;
97+
const {itemStates, ignoredItems, currentPage, targetPage, carouselOffset} = this.state;
11298
const {selectedIndex} = this.props;
11399
const clock = new Clock();
114100
const fromPage = new Value(selectedIndex);
115101
const toPage = new Value(selectedIndex);
102+
const isAnimating = new Value(0);
103+
const isScrolling = new Value(0);
116104

117105
return block([
118106
/* Page change by TabBar */
@@ -123,38 +111,48 @@ class TabController extends Component {
123111
cond(and(eq(state, State.END), !ignoredItem), [
124112
set(fromPage, toPage),
125113
set(toPage, index),
126-
set(this._targetPage, index)
114+
set(targetPage, index)
127115
]))
128116
];
129117
}),
130118

131-
cond(neq(this._currentPage, toPage),
132-
set(this._currentPage,
133-
timing({clock, from: fromPage, to: toPage, duration: 300, easing: Easing.bezier(0.34, 1.56, 0.64, 1)}))),
134-
135-
/* Page change by Carousel */
136-
onChange(this._carouselOffset, [
137-
cond(not(clockRunning(clock)), [
138-
set(this._currentPage,
139-
interpolate(round(this._carouselOffset), {
119+
// Animate currentPage to its target
120+
cond(neq(currentPage, toPage), [
121+
set(isAnimating, 1),
122+
set(currentPage,
123+
timing({clock, from: fromPage, to: toPage, duration: 300, easing: Easing.bezier(0.34, 1.56, 0.64, 1)}))
124+
]),
125+
// Set isAnimating flag off
126+
cond(and(eq(isAnimating, 1), not(clockRunning(clock))), set(isAnimating, 0)),
127+
128+
/* Page change by Carousel scroll */
129+
onChange(carouselOffset, [
130+
set(isScrolling, lessThan(round(abs(diff(carouselOffset))), round(Constants.screenWidth))),
131+
cond(and(not(isAnimating)), [
132+
set(currentPage,
133+
interpolate(round(carouselOffset), {
140134
inputRange: itemStates.map((v, i) => Math.round(i * Constants.screenWidth)),
141135
outputRange: itemStates.map((v, i) => i)
142136
})),
143-
set(toPage, this._currentPage),
144-
cond(eq(fract(this._currentPage), 0), set(this._targetPage, this._currentPage))
137+
set(toPage, currentPage)
145138
])
146139
]),
140+
// Update/Sync target page when scrolling is done
141+
cond(and(eq(isScrolling, 1), eq(floor(abs(diff(carouselOffset))), 0)), [
142+
set(isScrolling, 0),
143+
cond(not(between(fract(currentPage), 0.1, 0.9, 1)), set(targetPage, round(currentPage)))
144+
]),
147145

148146
/* Invoke index change */
149-
onChange(toPage, call([toPage], this.onPageChange))
147+
onChange(targetPage, call([targetPage], this.onPageChange))
150148
]);
151149
};
152150

153151
render() {
154152
const {itemStates} = this.state;
155153

156154
return (
157-
<TabBarContext.Provider value={this.getProviderContextValue()}>
155+
<TabBarContext.Provider value={this.state}>
158156
{this.props.children}
159157
{!_.isEmpty(itemStates) && <Code>{this.renderCodeBlock}</Code>}
160158
</TabBarContext.Provider>

0 commit comments

Comments
 (0)