Skip to content

Commit b747c91

Browse files
authored
Fix/tab controller transition (#1089)
* Fix first tab item change doesn't repsond * Memoize renderCodeBlock in TabController components
1 parent 4cbab00 commit b747c91

File tree

4 files changed

+105
-94
lines changed

4 files changed

+105
-94
lines changed

src/components/tabController/PageCarousel.tsx

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,20 @@ class PageCarousel extends PureComponent {
4040
}
4141
};
4242

43-
renderCodeBlock = () => {
43+
renderCodeBlock = _.memoize(() => {
4444
const {targetPage, containerWidth} = this.context;
45-
return block([
46-
Animated.onChange(targetPage, [call([targetPage], this.onTabChange)]),
47-
Animated.onChange(containerWidth, [call([targetPage], this.onTabChange)])
48-
]);
49-
};
45+
46+
return (
47+
<Code>
48+
{() =>
49+
block([
50+
Animated.onChange(targetPage, [call([targetPage], this.onTabChange)]),
51+
Animated.onChange(containerWidth, [call([targetPage], this.onTabChange)])
52+
])
53+
}
54+
</Code>
55+
);
56+
});
5057

5158
render() {
5259
const {selectedIndex, pageWidth} = this.context;
@@ -63,7 +70,7 @@ class PageCarousel extends PureComponent {
6370
contentOffset={{x: selectedIndex * pageWidth, y: 0}} // iOS only
6471
/>
6572

66-
<Code>{this.renderCodeBlock}</Code>
73+
{this.renderCodeBlock()}
6774
</>
6875
);
6976
}

src/components/tabController/TabBar.tsx

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ class TabBar extends PureComponent<Props, State> {
353353
const {itemsWidths} = this.state;
354354
const {indicatorStyle} = this.props;
355355
if (itemsWidths.length > 0) {
356-
return <Reanimated.View style={[styles.selectedIndicator, indicatorStyle, this._indicatorTransitionStyle]} />;
356+
return <Reanimated.View style={[styles.selectedIndicator, indicatorStyle, this._indicatorTransitionStyle]}/>;
357357
}
358358
}
359359

@@ -428,31 +428,23 @@ class TabBar extends PureComponent<Props, State> {
428428
}
429429
}
430430

431-
renderCodeBlock = () => {
431+
renderCodeBlock = _.memoize(() => {
432432
const {currentPage, targetPage} = this.context;
433433
const {itemsWidths, itemsOffsets} = this.state;
434434
const nodes = [];
435435

436-
nodes.push(
437-
set(
438-
this._indicatorOffset,
439-
interpolate(currentPage, {
440-
inputRange: itemsOffsets.map((_v, i) => i),
441-
outputRange: itemsOffsets
442-
})
443-
)
444-
);
445-
nodes.push(
446-
set(
447-
this._indicatorWidth,
448-
interpolate(currentPage, {inputRange: itemsWidths.map((_v, i) => i), outputRange: itemsWidths})
449-
)
450-
);
436+
nodes.push(set(this._indicatorOffset,
437+
interpolate(currentPage, {
438+
inputRange: itemsOffsets.map((_v, i) => i),
439+
outputRange: itemsOffsets
440+
})));
441+
nodes.push(set(this._indicatorWidth,
442+
interpolate(currentPage, {inputRange: itemsWidths.map((_v, i) => i), outputRange: itemsWidths})));
451443

452444
nodes.push(Reanimated.onChange(targetPage, Reanimated.call([targetPage], this.focusSelected)));
453445

454-
return block(nodes);
455-
};
446+
return <Code>{() => block(nodes)}</Code>;
447+
});
456448

457449
getShadowStyle() {
458450
const {enableShadow, shadowStyle} = this.props;
@@ -487,9 +479,9 @@ class TabBar extends PureComponent<Props, State> {
487479
</View>
488480
{this.renderSelectedIndicator()}
489481
</ScrollView>
490-
{_.size(itemsWidths) > 1 && <Code>{this.renderCodeBlock}</Code>}
491-
<ScrollBarGradient left visible={fadeLeft} />
492-
<ScrollBarGradient visible={fadeRight} />
482+
{_.size(itemsWidths) > 1 && this.renderCodeBlock()}
483+
<ScrollBarGradient left visible={fadeLeft}/>
484+
<ScrollBarGradient visible={fadeRight}/>
493485
</View>
494486
);
495487
}

src/components/tabController/TabPage.tsx

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,25 @@ export default class TabPage extends PureComponent<TabPageProps> {
6868
}, this.props.lazyLoadTime); // tab bar indicator transition time
6969
};
7070

71-
renderCodeBlock = () => {
71+
renderCodeBlock = _.memoize(() => {
7272
const {targetPage} = this.context;
7373
const {index, lazy} = this.props;
74-
return block([
75-
cond(and(eq(targetPage, index), Number(lazy), eq(this._loaded, 0)), [set(this._loaded, 1), call([], this.lazyLoad)]),
76-
cond(eq(targetPage, index),
77-
[set(this._opacity, 1), set(this._zIndex, 1)],
78-
[set(this._opacity, 0), set(this._zIndex, 0)])
79-
]);
80-
};
74+
return (
75+
<Code>
76+
{() =>
77+
block([
78+
cond(and(eq(targetPage, index), Number(lazy), eq(this._loaded, 0)), [
79+
set(this._loaded, 1),
80+
call([], this.lazyLoad)
81+
]),
82+
cond(eq(targetPage, index),
83+
[set(this._opacity, 1), set(this._zIndex, 1)],
84+
[set(this._opacity, 0), set(this._zIndex, 0)])
85+
])
86+
}
87+
</Code>
88+
);
89+
});
8190

8291
render() {
8392
const {renderLoading, testID} = this.props;
@@ -87,9 +96,7 @@ export default class TabPage extends PureComponent<TabPageProps> {
8796
<Reanimated.View style={this._pageStyle} testID={testID}>
8897
{!loaded && renderLoading?.()}
8998
{loaded && this.props.children}
90-
<Code>
91-
{this.renderCodeBlock}
92-
</Code>
99+
{this.renderCodeBlock()}
93100
</Reanimated.View>
94101
);
95102
}

src/components/tabController/index.tsx

Lines changed: 59 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const {
2424
diff,
2525
eq,
2626
floor,
27+
greaterThan,
2728
lessThan,
2829
neq,
2930
not,
@@ -159,75 +160,79 @@ class TabController extends Component<TabControllerProps, StateProps> {
159160
_.invoke(this.props, 'onChangeIndex', index);
160161
};
161162

162-
renderCodeBlock = () => {
163+
renderCodeBlock = _.memoize(() => {
163164
const {itemStates, ignoredItems, currentPage, targetPage, carouselOffset, containerWidth} = this.state;
164165
const {selectedIndex} = this.props;
165166
const clock = new Clock();
166167
const fromPage = new Value(selectedIndex);
167168
const toPage = new Value(selectedIndex);
168169
const isAnimating = new Value(0);
169170
const isScrolling = new Value(0);
171+
// temps
172+
const _carouselOffsetDiff = new Value(0);
170173

171-
return block([
172-
/* Page change by TabBar */
173-
..._.map(itemStates, (state, index) => {
174-
const ignoredItem = _.includes(ignoredItems, index);
175-
return [
176-
onChange(
177-
state,
178-
// @ts-ignore TODO: typescript?
179-
cond(and(eq(state, State.END), !ignoredItem), [
180-
set(fromPage, toPage),
181-
set(toPage, index),
182-
set(targetPage, index)
183-
])
184-
)
185-
];
186-
}),
187-
188-
// Animate currentPage to its target
189-
cond(neq(currentPage, toPage), [
190-
set(isAnimating, 1),
191-
set(
192-
currentPage,
193-
timing({clock, from: fromPage, to: toPage, duration: 280, easing: Easing.bezier(0.34, 1.3, 0.64, 1)})
194-
)
195-
]),
196-
// Set isAnimating flag off
197-
cond(and(eq(isAnimating, 1), not(clockRunning(clock))), set(isAnimating, 0)),
198-
199-
/* Page change by Carousel scroll */
200-
onChange(carouselOffset, [
201-
set(isScrolling, lessThan(round(abs(diff(carouselOffset))), round(containerWidth))),
202-
cond(not(isAnimating), [
203-
set(
204-
currentPage,
205-
interpolate(round(carouselOffset), {
206-
inputRange: itemStates.map((_v, i) => round(multiply(i, containerWidth))),
207-
outputRange: itemStates.map((_v, i) => i)
208-
})
209-
),
210-
set(toPage, currentPage)
211-
])
212-
]),
213-
// Update/Sync target page when scrolling is done
214-
cond(and(eq(isScrolling, 1), eq(floor(abs(diff(carouselOffset))), 0)), [
215-
set(isScrolling, 0),
216-
cond(not(between(fract(currentPage), 0.1, 0.9, true)), set(targetPage, round(currentPage)))
217-
]),
218-
219-
/* Invoke index change */
220-
onChange(targetPage, call([targetPage], this.onPageChange))
221-
]);
222-
};
174+
return (
175+
<Code>
176+
{() =>
177+
block([
178+
/* Page change by TabBar */
179+
..._.map(itemStates, (state, index) => {
180+
const ignoredItem = _.includes(ignoredItems, index);
181+
return [
182+
onChange(state,
183+
// @ts-ignore TODO: typescript?
184+
cond(and(eq(state, State.END), !ignoredItem), [
185+
set(fromPage, toPage),
186+
set(toPage, index),
187+
set(targetPage, index)
188+
]))
189+
];
190+
}),
191+
192+
// Animate currentPage to its target
193+
cond(neq(currentPage, toPage), [
194+
set(isAnimating, 1),
195+
set(currentPage,
196+
timing({clock, from: fromPage, to: toPage, duration: 280, easing: Easing.bezier(0.34, 1.3, 0.64, 1)}))
197+
]),
198+
// Set isAnimating flag off
199+
cond(and(eq(isAnimating, 1), not(clockRunning(clock))), set(isAnimating, 0)),
200+
201+
/* Page change by Carousel scroll */
202+
onChange(carouselOffset, [
203+
set(_carouselOffsetDiff, round(abs(diff(carouselOffset)))),
204+
set(isScrolling,
205+
and(lessThan(_carouselOffsetDiff, round(containerWidth)), greaterThan(_carouselOffsetDiff, 0))),
206+
cond(not(isAnimating), [
207+
set(currentPage,
208+
interpolate(round(carouselOffset), {
209+
inputRange: itemStates.map((_v, i) => round(multiply(i, containerWidth))),
210+
outputRange: itemStates.map((_v, i) => i)
211+
})),
212+
set(toPage, currentPage)
213+
])
214+
]),
215+
// Update/Sync target page when scrolling is done
216+
cond(and(eq(isScrolling, 1), eq(floor(abs(diff(carouselOffset))), 0)), [
217+
set(isScrolling, 0),
218+
cond(not(between(fract(currentPage), 0.1, 0.9, true)), set(targetPage, round(currentPage)))
219+
]),
220+
221+
/* Invoke index change */
222+
onChange(targetPage, call([targetPage], this.onPageChange))
223+
])
224+
}
225+
</Code>
226+
);
227+
});
223228

224229
render() {
225230
const {itemStates} = this.state;
226231

227232
return (
228233
<TabBarContext.Provider value={this.state}>
229234
{this.props.children}
230-
{!_.isEmpty(itemStates) && <Code>{this.renderCodeBlock}</Code>}
235+
{!_.isEmpty(itemStates) && this.renderCodeBlock()}
231236
</TabBarContext.Provider>
232237
);
233238
}

0 commit comments

Comments
 (0)