Skip to content

Commit b7735b0

Browse files
Infra/Migrate Incubator.WheelPicker to reanimated 2 (#1379)
* Feat/Add Haptic to Incubator WheelPicker * remove unneccesary change * resolve conflicts * update haptic timing * minor formatting fix * migrate to reanimated 2 * typings * use useAnimatedReaction hook * use useAnimatedReaction hook * improve performance * Migrate Incubator.WheelPicker to reanimated 2 Co-authored-by: Ethan Sharabi <[email protected]>
1 parent b1c7aaf commit b7735b0

File tree

4 files changed

+57
-61
lines changed

4 files changed

+57
-61
lines changed

demo/src/screens/incubatorScreens/WheelPickerScreen.tsx

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, {useCallback, useState} from 'react';
1+
import React, {useCallback, useMemo, useState} from 'react';
22
import {View, Text, Incubator, Colors, Typography, Button, Dialog} from 'react-native-ui-lib';
33
import _ from 'lodash';
44

@@ -29,42 +29,42 @@ const useData = (initialMonth?: string, initialYear?: string, initialDays?: numb
2929
const [selectedDays, setDays] = useState<number | undefined>(initialDays);
3030
const [showDialog, setShowDialog] = useState(false);
3131

32-
const onPickDaysPress = () => {
32+
const onPickDaysPress = useCallback(() => {
3333
setShowDialog(true);
34-
};
34+
}, []);
3535

36-
const onDialogDismissed = () => {
36+
const onDialogDismissed = useCallback(() => {
3737
setShowDialog(false);
38-
};
38+
}, []);
3939

40-
const onMonthChange = (item: string | undefined, _: number) => {
40+
const onMonthChange = useCallback((item: string | undefined, _: number) => {
4141
setMonth(item);
42-
};
42+
}, []);
4343

44-
const onYearChange = (item: string | undefined, _: number) => {
44+
const onYearChange = useCallback((item: string | undefined, _: number) => {
4545
setYear(item);
46-
};
46+
}, []);
4747

48-
const onDaysChange = (item: number | undefined, _: number) => {
48+
const onDaysChange = useCallback((item: number | undefined, _: number) => {
4949
setDays(item);
50-
};
50+
}, []);
5151

52-
const getMonths = useCallback(() => {
52+
const monthItems = useMemo(() => {
5353
return _.map(months, item => ({label: item, value: item}));
5454
}, []);
5555

56-
const getYears = useCallback(() => {
56+
const yearItems = useMemo(() => {
5757
return _.map(years, item => ({label: '' + item, value: item}));
5858
}, []);
5959

60-
const getDays = useCallback(() => {
60+
const dayItems = useMemo(() => {
6161
return _.map(days, item => ({label: '' + item, value: item}));
6262
}, []);
6363

6464
return {
65-
getMonths,
66-
getYears,
67-
getDays,
65+
monthItems,
66+
yearItems,
67+
dayItems,
6868
onMonthChange,
6969
onYearChange,
7070
onDaysChange,
@@ -81,13 +81,13 @@ export default () => {
8181
const {
8282
selectedMonth,
8383
onMonthChange,
84-
getMonths,
84+
monthItems,
8585
selectedYear,
8686
onYearChange,
87-
getYears,
87+
yearItems,
8888
selectedDays,
8989
onDaysChange,
90-
getDays,
90+
dayItems,
9191
onPickDaysPress,
9292
onDialogDismissed,
9393
showDialog
@@ -104,7 +104,7 @@ export default () => {
104104
onChange={onMonthChange}
105105
activeTextColor={Colors.primary}
106106
inactiveTextColor={Colors.grey20}
107-
items={getMonths()}
107+
items={monthItems}
108108
textStyle={Typography.text60R}
109109
selectedValue={selectedMonth}
110110
/>
@@ -115,18 +115,13 @@ export default () => {
115115
onChange={onYearChange}
116116
numberOfVisibleRows={3}
117117
selectedValue={selectedYear}
118-
items={getYears()}
118+
items={yearItems}
119119
/>
120120
</View>
121121

122-
<Button marginT-40 label={'Pick Days'} marginH-120 onPress={onPickDaysPress}/>
122+
<Button marginT-40 label={'Pick Days'} marginH-100 onPress={onPickDaysPress}/>
123123
<Dialog width={'90%'} height={260} bottom visible={showDialog} onDismiss={onDialogDismissed}>
124-
<Incubator.WheelPicker
125-
onChange={onDaysChange}
126-
selectedValue={selectedDays}
127-
label={'Days'}
128-
items={getDays()}
129-
/>
124+
<Incubator.WheelPicker onChange={onDaysChange} selectedValue={selectedDays} label={'Days'} items={dayItems}/>
130125
</Dialog>
131126
</View>
132127
</View>
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
/// <reference types="react" />
1+
import React from 'react';
22
import { TextStyle } from 'react-native';
3+
import Animated from 'react-native-reanimated';
34
export interface ItemProps {
45
label: string;
56
value: string | number;
67
}
78
interface InternalProps extends ItemProps {
89
index: number;
9-
offset: any;
10+
offset: Animated.SharedValue<number>;
1011
itemHeight: number;
1112
activeColor?: string;
1213
inactiveColor?: string;
@@ -15,5 +16,5 @@ interface InternalProps extends ItemProps {
1516
testID?: string;
1617
centerH?: boolean;
1718
}
18-
declare const _default: ({ index, label, itemHeight, onSelect, offset, activeColor, inactiveColor, style, testID, centerH }: InternalProps) => JSX.Element;
19+
declare const _default: React.MemoExoticComponent<({ index, label, itemHeight, onSelect, offset, activeColor, inactiveColor, style, testID, centerH }: InternalProps) => JSX.Element>;
1920
export default _default;

src/incubator/WheelPicker/Item.tsx

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import React, {useCallback, useMemo} from 'react';
1+
import React, {useCallback, useMemo, memo} from 'react';
22
import {TextStyle, StyleSheet} from 'react-native';
3-
import Animated, {interpolateColors} from 'react-native-reanimated';
3+
import Animated, {interpolateColor, useAnimatedStyle} from 'react-native-reanimated';
44
import Text from '../../components/text';
55
import TouchableOpacity from '../../components/touchableOpacity';
66
import {Colors, Spacings} from '../../../src/style';
@@ -15,7 +15,7 @@ export interface ItemProps {
1515

1616
interface InternalProps extends ItemProps {
1717
index: number;
18-
offset: any;
18+
offset: Animated.SharedValue<number>;
1919
itemHeight: number;
2020
activeColor?: string;
2121
inactiveColor?: string;
@@ -25,7 +25,7 @@ interface InternalProps extends ItemProps {
2525
centerH?: boolean;
2626
}
2727

28-
export default ({
28+
export default memo(({
2929
index,
3030
label,
3131
itemHeight,
@@ -40,11 +40,11 @@ export default ({
4040
const selectItem = useCallback(() => onSelect(index), [index]);
4141
const itemOffset = index * itemHeight;
4242

43-
const color = useMemo(() => {
44-
return interpolateColors(offset, {
45-
inputRange: [itemOffset - itemHeight, itemOffset, itemOffset + itemHeight],
46-
outputColorRange: [inactiveColor, activeColor, inactiveColor]
47-
});
43+
const animatedColorStyle = useAnimatedStyle(() => {
44+
const color = interpolateColor(offset.value,
45+
[itemOffset - itemHeight, itemOffset, itemOffset + itemHeight],
46+
[inactiveColor, activeColor, inactiveColor]);
47+
return {color};
4848
}, [itemHeight]);
4949

5050
const containerStyle = useMemo(() => {
@@ -64,13 +64,12 @@ export default ({
6464
index={index}
6565
testID={testID}
6666
>
67-
{/* @ts-ignore reanimated2*/}
68-
<AnimatedText text60R style={{color, ...style}}>
67+
<AnimatedText text60R style={[animatedColorStyle, style]}>
6968
{label}
7069
</AnimatedText>
7170
</AnimatedTouchableOpacity>
7271
);
73-
};
72+
});
7473

7574
const styles = StyleSheet.create({
7675
container: {

src/incubator/WheelPicker/index.tsx

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
// TODO: Support style customization
22
import React, {useCallback, useRef, useMemo, useEffect, useState} from 'react';
33
import {TextStyle, ViewStyle, FlatList, NativeSyntheticEvent, NativeScrollEvent, StyleSheet} from 'react-native';
4-
import Animated from 'react-native-reanimated';
5-
import {onScrollEvent, useValues} from 'react-native-redash';
4+
import Animated, {useSharedValue, useAnimatedScrollHandler} from 'react-native-reanimated';
65
import {Colors, Spacings} from 'style';
76
import View from '../../components/view';
87
import Fader, {FaderPosition} from '../../components/fader';
@@ -88,8 +87,10 @@ const WheelPicker = React.memo(({
8887
testID
8988
}: WheelPickerProps) => {
9089
const scrollView = useRef<Animated.ScrollView>();
91-
const [offset] = useValues([0], []);
92-
const onScroll = onScrollEvent({y: offset});
90+
const offset = useSharedValue(0);
91+
const scrollHandler = useAnimatedScrollHandler(e => {
92+
offset.value = e.contentOffset.y;
93+
});
9394

9495
const {
9596
height,
@@ -112,6 +113,8 @@ const WheelPicker = React.memo(({
112113
controlComponent();
113114
});
114115

116+
const keyExtractor = useCallback((item: ItemProps, index: number) => `${item}.${index}`, []);
117+
115118
/**
116119
* The picker is a controlled component. This means we expect the
117120
* to relay on `selectedValue` prop to be our
@@ -124,9 +127,9 @@ const WheelPicker = React.memo(({
124127
}
125128
};
126129

127-
const scrollToPassedIndex = () => {
130+
const scrollToPassedIndex = useCallback(() => {
128131
scrollToIndex(currentIndex, false);
129-
};
132+
}, []);
130133

131134
const scrollToIndex = (index: number, animated: boolean) => {
132135
// this is done to handle onMomentumScrollEnd not being called in Android:
@@ -144,12 +147,12 @@ const WheelPicker = React.memo(({
144147
},
145148
[itemHeight]);
146149

147-
const onValueChange = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
150+
const onValueChange = useCallback((event: NativeSyntheticEvent<NativeScrollEvent>) => {
148151
setScrollOffset(event.nativeEvent.contentOffset.y);
149152

150153
const {index, value} = getRowItemAtOffset(event.nativeEvent.contentOffset.y);
151154
onChange?.(value, index);
152-
};
155+
}, []);
153156

154157
const renderItem = useCallback(({item, index}) => {
155158
return (
@@ -169,23 +172,23 @@ const WheelPicker = React.memo(({
169172
},
170173
[itemHeight]);
171174

172-
const renderSeparators = () => {
175+
const separators = useMemo(() => {
173176
return (
174177
<View absF centerV pointerEvents="none">
175178
<View style={styles.separators}/>
176179
</View>
177180
);
178-
};
181+
}, []);
179182

180-
const renderLabel = () => {
183+
const labelContainer = useMemo(() => {
181184
return (
182185
<View centerV>
183186
<Text marginL-s2 text80M {...labelProps} color={activeTextColor} style={labelStyle}>
184187
{label}
185188
</Text>
186189
</View>
187190
);
188-
};
191+
}, []);
189192

190193
const fader = useMemo(() => (position: FaderPosition) => {
191194
return <Fader visible position={position} size={60}/>;
@@ -212,7 +215,7 @@ const WheelPicker = React.memo(({
212215
// @ts-ignore reanimated2
213216
keyExtractor={keyExtractor}
214217
scrollEventThrottle={100}
215-
onScroll={onScroll}
218+
onScroll={scrollHandler}
216219
onMomentumScrollEnd={onValueChange}
217220
showsVerticalScrollIndicator={false}
218221
onLayout={scrollToPassedIndex}
@@ -226,17 +229,15 @@ const WheelPicker = React.memo(({
226229
initialScrollIndex={currentIndex}
227230
/>
228231
</View>
229-
{label && renderLabel()}
232+
{label && labelContainer}
230233
</View>
231234
{fader(FaderPosition.BOTTOM)}
232235
{fader(FaderPosition.TOP)}
233-
{renderSeparators()}
236+
{separators}
234237
</View>
235238
);
236239
});
237240

238-
const keyExtractor = (item: ItemProps) => `${item.value}`;
239-
240241
export default WheelPicker;
241242

242243
const styles = StyleSheet.create({

0 commit comments

Comments
 (0)