Skip to content

Commit a38dab3

Browse files
authored
WheelPicker (incubator) - fix updates (#1693)
* WheelPicker (incubator) - allow 'item' change; block 'onChange' call when 'initialValue' changes * organize code
1 parent 7bb60c0 commit a38dab3

File tree

2 files changed

+67
-60
lines changed

2 files changed

+67
-60
lines changed

src/incubator/WheelPicker/index.tsx

Lines changed: 65 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ import {isFunction, isUndefined} from 'lodash';
33
import React, {useCallback, useRef, useMemo, useEffect, useState} from 'react';
44
import {TextStyle, ViewStyle, FlatList, NativeSyntheticEvent, NativeScrollEvent, StyleSheet} from 'react-native';
55
import Animated, {useSharedValue, useAnimatedScrollHandler} from 'react-native-reanimated';
6+
import {Constants} from 'helpers';
67
import {Colors, Spacings} from 'style';
8+
import {asBaseComponent} from '../../commons/new';
79
import View from '../../components/view';
810
import Fader, {FaderPosition} from '../../components/fader';
9-
import {Constants} from 'helpers';
1011
import Item, {ItemProps} from './Item';
11-
import usePresenter from './usePresenter';
1212
import Text, {TextProps} from '../../components/text';
13-
import {asBaseComponent} from '../../commons/new';
13+
import usePresenter from './usePresenter';
1414

1515
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
1616

@@ -125,26 +125,46 @@ const WheelPicker = ({
125125
preferredNumVisibleRows: numberOfVisibleRows
126126
});
127127

128+
const prevInitialValue = useRef(initialValue);
128129
const prevIndex = useRef(currentIndex);
129130
const [scrollOffset, setScrollOffset] = useState(currentIndex * itemHeight);
130131
const [flatListWidth, setFlatListWidth] = useState(0);
131132
const keyExtractor = useCallback((item: ItemProps, index: number) => `${item}.${index}`, []);
132133

133-
/* This effect enforce the index to be controlled by selectedValue passed by the user */
134134
useEffect(() => {
135+
// This effect enforce the index to be controlled by selectedValue passed by the user
135136
if (shouldControlComponent(scrollOffset)) {
136137
scrollToIndex(currentIndex, true);
137138
}
138139
});
139140

140-
/* This effect making sure to reset index if initialValue has changed */
141141
useEffect(() => {
142+
// This effect making sure to reset index if initialValue has changed
142143
!isUndefined(initialValue) && scrollToIndex(currentIndex, true);
143144
}, [currentIndex]);
144145

145-
const scrollToPassedIndex = useCallback(() => {
146-
scrollToIndex(currentIndex, false);
147-
}, []);
146+
const _onChange = useCallback((value: string | number, index: number) => {
147+
if (prevInitialValue.current !== initialValue) {
148+
// don't invoke 'onChange' if 'initialValue' changed
149+
prevInitialValue.current = initialValue;
150+
} else {
151+
onChange?.(value, index);
152+
}
153+
}, [initialValue, onChange]);
154+
155+
const onValueChange = useCallback((event: NativeSyntheticEvent<NativeScrollEvent>) => {
156+
setScrollOffset(event.nativeEvent.contentOffset.y);
157+
const {index, value} = getRowItemAtOffset(event.nativeEvent.contentOffset.y);
158+
_onChange(value, index);
159+
}, [_onChange, getRowItemAtOffset]);
160+
161+
const onMomentumScrollEndAndroid = (index: number) => {
162+
// handle Android bug: ScrollView does not call 'onMomentumScrollEnd' when scrolled programmatically (https://github.com/facebook/react-native/issues/26661)
163+
if (Constants.isAndroid && prevIndex.current !== index) {
164+
prevIndex.current = index;
165+
_onChange(items?.[index]?.value, index);
166+
}
167+
};
148168

149169
const scrollToOffset = (index: number, animated: boolean) => {
150170
// TODO: we should remove this split (the getNode section) in V6 and remove support for reanimated 1
@@ -159,35 +179,17 @@ const WheelPicker = ({
159179
};
160180

161181
const scrollToIndex = (index: number, animated: boolean) => {
162-
// this is done to handle onMomentumScrollEnd not being called in Android:
163-
// https://github.com/facebook/react-native/issues/26661
164-
if (Constants.isAndroid && prevIndex.current !== index) {
165-
prevIndex.current = index;
166-
onChange?.(items?.[index]?.value, index);
167-
}
182+
onMomentumScrollEndAndroid(index);
168183
setTimeout(() => scrollToOffset(index, animated), 100);
169184
};
170185

186+
const scrollToPassedIndex = useCallback(() => {
187+
scrollToIndex(currentIndex, false);
188+
}, []);
189+
171190
const selectItem = useCallback(index => {
172191
scrollToIndex(index, true);
173-
},
174-
[itemHeight]);
175-
176-
const onValueChange = useCallback((event: NativeSyntheticEvent<NativeScrollEvent>) => {
177-
setScrollOffset(event.nativeEvent.contentOffset.y);
178-
179-
const {index, value} = getRowItemAtOffset(event.nativeEvent.contentOffset.y);
180-
onChange?.(value, index);
181-
},
182-
[onChange]);
183-
184-
const alignmentStyle = useMemo(() => {
185-
return align === WheelPickerAlign.RIGHT
186-
? {alignSelf: undefined}
187-
: align === WheelPickerAlign.LEFT
188-
? {alignSelf: 'flex-start'}
189-
: {alignSelf: 'center'};
190-
}, [align]);
192+
}, [itemHeight]);
191193

192194
const renderItem = useCallback(({item, index}) => {
193195
return (
@@ -207,17 +209,33 @@ const WheelPicker = ({
207209
testID={`${testID}.item_${index}`}
208210
/>
209211
);
210-
},
211-
[itemHeight]);
212+
}, [itemHeight]);
212213

213-
const separators = useMemo(() => {
214-
return (
215-
<View absF centerV pointerEvents="none">
216-
<View style={styles.separators}/>
217-
</View>
218-
);
214+
const getItemLayout = useCallback((_data, index: number) => {
215+
return {length: itemHeight, offset: itemHeight * index, index};
216+
}, [itemHeight]);
217+
218+
const updateFlatListWidth = useCallback((width: number) => {
219+
setFlatListWidth(width);
219220
}, []);
220221

222+
const alignmentStyle = useMemo(() => {
223+
return align === WheelPickerAlign.RIGHT
224+
? {alignSelf: undefined}
225+
: align === WheelPickerAlign.LEFT
226+
? {alignSelf: 'flex-start'}
227+
: {alignSelf: 'center'};
228+
}, [align]);
229+
230+
const contentContainerStyle = useMemo(() => {
231+
return [
232+
{
233+
paddingVertical: height / 2 - itemHeight / 2
234+
},
235+
alignmentStyle
236+
];
237+
}, [height, itemHeight, alignmentStyle]);
238+
221239
const labelContainerStyle = useMemo(() => {
222240
return [{position: 'absolute', top: 0, bottom: 0}, alignmentStyle];
223241
}, [alignmentStyle]);
@@ -237,26 +255,15 @@ const WheelPicker = ({
237255

238256
const fader = useMemo(() => (position: FaderPosition) => {
239257
return <Fader visible position={position} size={60}/>;
240-
},
241-
[]);
242-
243-
const getItemLayout = useCallback((_data, index: number) => {
244-
return {length: itemHeight, offset: itemHeight * index, index};
245-
},
246-
[itemHeight]);
247-
248-
const updateFlatListWidth = useCallback((width: number) => {
249-
setFlatListWidth(width);
250258
}, []);
251259

252-
const contentContainerStyle = useMemo(() => {
253-
return [
254-
{
255-
paddingVertical: height / 2 - itemHeight / 2
256-
},
257-
alignmentStyle
258-
];
259-
}, [height, itemHeight, alignmentStyle]);
260+
const separators = useMemo(() => {
261+
return (
262+
<View absF centerV pointerEvents="none">
263+
<View style={styles.separators}/>
264+
</View>
265+
);
266+
}, []);
260267

261268
return (
262269
<View testID={testID} bg-white style={style}>

src/incubator/WheelPicker/usePresenter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import React, {useRef} from 'react';
21
import _ from 'lodash';
2+
import React from 'react';
33
import {ItemProps} from './Item';
44
import useMiddleIndex from './helpers/useListMiddleIndex';
55

@@ -44,7 +44,7 @@ const usePresenter = ({
4444
return items || [];
4545
};
4646

47-
const items = useRef<ItemProps[]>(children ? extractItemsFromChildren() : propItems!).current;
47+
const items = children ? extractItemsFromChildren() : propItems || [];
4848
const middleIndex = useMiddleIndex({itemHeight, listSize: items.length});
4949

5050
const getSelectedValueIndex = () => {

0 commit comments

Comments
 (0)