react-native-wheel-picker
react-native-wheel-picker copied to clipboard
Android picker does not scroll back to desired index
I'm making a basic time picker with two wheels: hour, minutes. If the selected time is not valid, the wheel should turn back to the previously valid time.
For example, let's say it is 10:30am so valid times are 10:30am - 11:59pm. If the user selected 10:15, I expect the minute wheel to scroll back to 10:30.
This is working fine on iOS, but on Android, the wheel simply stays at the invalid index.
So my question is: how to enable programmatic scroll to an index of the picker on Android to achieve the same behavior as iOS?
Thanks :)
Env: react-native 0.57.8 react-native-wheel-picker 1.2.0
So basically I guess the problem is the picker does not rerender when the props used for selectedValue
have not changed. This is really weird because I see that the underlying <WheelCurvedPicker>
props for selectedValue
do change.
I have a similar problem but in iOS. In fact, i'm doing the same input (HH:MM) with 2 scrolls. My problem is after scroll and select any hour or minute, the input is scroll down automatically to the initial value at the bottom (the value is updated in my local state, but after select, there is another scroll that i cannot manage, it is done without any interaction).
Any idea?
@sebackend maybe you have some mistake in your onValueChange
call, or how you call setState
Happy to help if u share code. However, I don't think this should be discussed in here as it sound like a somewhat different problem, plus its iOS and this issue thread is on Android only.
So basically I guess the problem is the picker does not rerender when the props used for
selectedValue
have not changed. This is really weird because I see that the underlying<WheelCurvedPicker>
props forselectedValue
do change.
Hey @LaVielle I have kind of that issue on an Android device (ios works fine). Did you manage to resolve your issue?
@riki-gertzik For us the solution was to only show valid data. So when the user mounts this picker wheel, we calculate valid times and generate an array to populate the picker. Then, when the user makes a selection, we check if the time is valid (slightly differently for iOS and Android). This is the function we use to handle picker selection, wrote almost a year a go so I don't fully understand it anymore, but hopefully you can get something out of it.
Note: the quickSelector
you'll see bellow is some other UI element that allows user to pick relative time (eg. "in 15 mins"), and is not related to the time picker wheel
/**
* selectedPicker: {
* hour: String // "00" - "23"
* minute: String // "00" - "59"
* value: Moment // the full time/date for that valid time
* }
*/
handleTimePickerChange = (selectedPicker) => {
const { validPickupTimesForDay, lastQuickSelectorReset, quickSelector } = this.state
let { selectedHour, selectedMinute, selectedTime } = this.state
// assumes we should reset quickSelector because iOS functions as expected:
// onValueChange fires only on manual change
let resetQuickSelector = true
// 'debounce' quickSelector reset on Android:
// onValueChange fires when Picker values are changed programmatically (not expected)
// this happens only on Android, so we check if the last quickSelector change was long ago and reset it only then
if (Platform.OS === 'android') {
const now = moment().subtract(1500, 'ms')
resetQuickSelector = quickSelector !== null && now.isAfter(lastQuickSelectorReset)
}
const validTime = this.checkValidity(validPickupTimesForDay, selectedPicker)
if (validTime) {
if (selectedPicker.type === 'hour') {
selectedHour = selectedPicker
selectedTime = validTime
}
if (selectedPicker.type === 'minute') {
selectedMinute = selectedPicker
selectedTime = validTime
}
} else if (selectedPicker.type === 'hour' && selectedPicker.index === 0) {
// hour:minute combination is not valid, so reset minutes to soonest valid minute
selectedMinute = {
label: validPickupTimesForDay[0].minute,
index: validPickupTimesForDay[0].value.get('minutes'),
}
selectedTime = this.getValidTime(validPickupTimesForDay, selectedPicker.label, selectedMinute.label)
selectedHour = selectedPicker
}
this.setState({
selectedHour,
selectedMinute,
selectedTime,
quickSelector: resetQuickSelector ? null : quickSelector,
})
}
@riki-gertzik For us the solution was to only show valid data. So when the user mounts this picker wheel, we calculate valid times and generate an array to populate the picker. Then, when the user makes a selection, we check if the time is valid (slightly differently for iOS and Android). This is the function we use to handle picker selection, wrote almost a year a go so I don't fully understand it anymore, but hopefully you can get something out of it.
Note: the
quickSelector
you'll see bellow is some other UI element that allows user to pick relative time (eg. "in 15 mins"), and is not related to the time picker wheel/** * selectedPicker: { * hour: String // "00" - "23" * minute: String // "00" - "59" * value: Moment // the full time/date for that valid time * } */ handleTimePickerChange = (selectedPicker) => { const { validPickupTimesForDay, lastQuickSelectorReset, quickSelector } = this.state let { selectedHour, selectedMinute, selectedTime } = this.state // assumes we should reset quickSelector because iOS functions as expected: // onValueChange fires only on manual change let resetQuickSelector = true // 'debounce' quickSelector reset on Android: // onValueChange fires when Picker values are changed programmatically (not expected) // this happens only on Android, so we check if the last quickSelector change was long ago and reset it only then if (Platform.OS === 'android') { const now = moment().subtract(1500, 'ms') resetQuickSelector = quickSelector !== null && now.isAfter(lastQuickSelectorReset) } const validTime = this.checkValidity(validPickupTimesForDay, selectedPicker) if (validTime) { if (selectedPicker.type === 'hour') { selectedHour = selectedPicker selectedTime = validTime } if (selectedPicker.type === 'minute') { selectedMinute = selectedPicker selectedTime = validTime } } else if (selectedPicker.type === 'hour' && selectedPicker.index === 0) { // hour:minute combination is not valid, so reset minutes to soonest valid minute selectedMinute = { label: validPickupTimesForDay[0].minute, index: validPickupTimesForDay[0].value.get('minutes'), } selectedTime = this.getValidTime(validPickupTimesForDay, selectedPicker.label, selectedMinute.label) selectedHour = selectedPicker } this.setState({ selectedHour, selectedMinute, selectedTime, quickSelector: resetQuickSelector ? null : quickSelector, }) }
thanks @LaVielle for your response, our problem is that we have one wheel for degrees, where you can switch between C and F but if the new item "value" is the same as the old one ("value" = index), I don't see the new items but the old items until I do some change - press or start scrolling.
anyway, seems like a different problem.. I think it's related to when onValueChange
called too but I'm not sure.