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

Commit 116dc0b

Browse files
committed
fix: try to improve the performance
This commit aims to throttle the events from reanimated to avoid overloading JS thread. It changes a few things: - Due to the throttling, we cannot get the same scroll as you swipe effect on scrollable tab bar, which is probably better as this didn't work smoothly on lower end devices - We now call onIndexChange as soon as you lift your finger rather than waiting for the animation to finish, which seems to work okay except a lag on Android - Due to throttling, empty page for lazy loaded tabs is more pronounched, but this can be work around by providing a placeholder component Closes #700, #716
1 parent ad9e39a commit 116dc0b

File tree

2 files changed

+31
-54
lines changed

2 files changed

+31
-54
lines changed

src/Pager.js

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ const {
5555
greaterThan,
5656
max,
5757
min,
58+
modulo,
5859
multiply,
5960
neq,
6061
or,
@@ -93,6 +94,8 @@ const TIMING_CONFIG = {
9394
easing: Easing.out(Easing.cubic),
9495
};
9596

97+
const THROTTLE_FACTOR = 16;
98+
9699
export default class Pager<T: Route> extends React.Component<Props<T>> {
97100
static defaultProps = {
98101
swipeVelocityThreshold: 1200,
@@ -203,6 +206,8 @@ export default class Pager<T: Route> extends React.Component<Props<T>> {
203206
_gestureState = new Value(State.UNDETERMINED);
204207
_offsetX = new Value(0);
205208

209+
_throttler = new Animated.Value(0);
210+
206211
// Current position of the page (translateX value)
207212
_position = new Value(
208213
// Initial value is based on the index and page width
@@ -406,20 +411,6 @@ export default class Pager<T: Route> extends React.Component<Props<T>> {
406411
set(this._velocityX, 0),
407412
// When the animation finishes, stop the clock
408413
stopClock(this._clock),
409-
call([this._index], ([value]) => {
410-
// If the index changed, and previous animation has finished, update state
411-
this.props.onIndexChange(value);
412-
413-
// Without this check, the pager can go to an infinite update <-> animate loop for sync updates
414-
if (value !== this.props.navigationState.index) {
415-
this._pendingIndexValue = value;
416-
417-
// Force componentDidUpdate to fire, whether user does a setState or not
418-
// This allows us to detect when the user drops the update and revert back
419-
// It's necessary to make sure that the state stays in sync
420-
this.forceUpdate();
421-
}
422-
}),
423414
]),
424415
]);
425416
};
@@ -435,18 +426,37 @@ export default class Pager<T: Route> extends React.Component<Props<T>> {
435426
]);
436427

437428
_translateX = block([
438-
call([this._index], ([value]) => {
439-
this._currentIndexValue = value;
440-
}),
429+
onChange(
430+
this._index,
431+
call([this._index], ([value]) => {
432+
this._currentIndexValue = value;
433+
434+
// Without this check, the pager can go to an infinite update <-> animate loop for sync updates
435+
if (value !== this.props.navigationState.index) {
436+
// If the index changed, and previous animation has finished, update state
437+
this.props.onIndexChange(value);
438+
439+
this._pendingIndexValue = value;
440+
441+
// Force componentDidUpdate to fire, whether user does a setState or not
442+
// This allows us to detect when the user drops the update and revert back
443+
// It's necessary to make sure that the state stays in sync
444+
this.forceUpdate();
445+
}
446+
})
447+
),
441448
// Conditionally listen for changes in the position value
442449
// The position value can changes a lot in short time
443450
// So we only add a listener when necessary to avoid extra overhead
444451
cond(
445452
this._isListening,
446-
onChange(
447-
this._position,
448-
call([this._position], this._handlePositionChange)
449-
)
453+
onChange(this._position, [
454+
cond(
455+
eq(this._throttler, 0),
456+
call([this._position], this._handlePositionChange)
457+
),
458+
set(this._throttler, modulo(add(this._throttler, 1), THROTTLE_FACTOR)),
459+
])
450460
),
451461
onChange(
452462
this._isSwiping,

src/TabBar.js

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,6 @@ export default class TabBar<T: Route> extends React.Component<Props<T>, State> {
9595
};
9696
}
9797

98-
componentDidMount() {
99-
if (this.props.scrollEnabled) {
100-
this.props.addListener('position', this._adjustScroll);
101-
}
102-
}
103-
10498
componentDidUpdate(prevProps: Props<T>) {
10599
if (
106100
prevProps.navigationState.routes.length !==
@@ -113,18 +107,6 @@ export default class TabBar<T: Route> extends React.Component<Props<T>, State> {
113107
) {
114108
this._resetScroll(this.props.navigationState.index);
115109
}
116-
117-
if (prevProps.scrollEnabled !== this.props.scrollEnabled) {
118-
if (this.props.scrollEnabled) {
119-
this.props.addListener('position', this._adjustScroll);
120-
} else {
121-
this.props.removeListener('position', this._adjustScroll);
122-
}
123-
}
124-
}
125-
126-
componentWillUnmount() {
127-
this.props.removeListener('position', this._adjustScroll);
128110
}
129111

130112
_scrollView: ?ScrollView;
@@ -184,21 +166,6 @@ export default class TabBar<T: Route> extends React.Component<Props<T>, State> {
184166
return this._normalizeScrollValue(props, scrollAmount);
185167
};
186168

187-
_adjustScroll = (value: number) => {
188-
if (this.props.scrollEnabled) {
189-
cancelAnimationFrame(this._scrollResetCallback);
190-
191-
this._scrollView &&
192-
this._scrollView.scrollTo({
193-
x: this._normalizeScrollValue(
194-
this.props,
195-
this._getScrollAmount(this.props, value)
196-
),
197-
animated: false,
198-
});
199-
}
200-
};
201-
202169
_resetScroll = (value: number, animated = true) => {
203170
if (this.props.scrollEnabled) {
204171
cancelAnimationFrame(this._scrollResetCallback);

0 commit comments

Comments
 (0)