Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

in IOS, onValueChange shouldn't be triggered when only scroll item list #581

Open
saaspeter opened this issue May 25, 2024 · 4 comments
Open

Comments

@saaspeter
Copy link

saaspeter commented May 25, 2024

Is your feature request related to a problem? Please describe.

in IOS, since there is a modal picker, when the user scroll the values, but not press "Done" button, the TextInput value will also change. This will lead to unexpected strange behaviors. As the below video shows: if the UI elements will change based on the picker new selected value, when the user only scroll the item list (not press Done button), the UI elements will change dramatically, it is a different behavior with native IOS select pickers.
If I want to change the behavior, I will add the following element behaviors logic in onDonePress method, but there is also a strange behavior, because if the user scroll the list to a new value item, but not press Done button, instead the user press other space to close the picker modal, then the TextInput value is a new selected value, but other elements in the UI remains old (not change). I need carefully handle the behaviors.

Describe the solution you'd like

in IOS, if the user only scroll the item list, do not change the value, also do not trigger onValueChange, thus do not change TextInput value, only when user press "Done", the select value will be shown in TextInput.

select_issue.mov

Describe alternatives you've considered

I write a component to wrap RNPickerSelect, the code as below: introduce a internal value to store the scrolled value, only when "Done" pressed, the follow behavior will change ,but there is a strange behavior: when the user scroll the list to a new value, the TextInput will show new value, but when user press outer space to close the modal, the value in TextInput will change to its old value automatically.

`
interface PickerProps extends Omit<PickerSelectProps, "onValueChange">{
showPicker?: boolean;
onDoneValueCallback?: (value: any, index?: number) => void;
}

export const PickerSelect : React.FC =
function PickerSelect(_props) {

const pickerRef = React.useRef();
const [selectedIndex, setSelectedIndex] = React.useState()
const { onDoneValueCallback, onDonePress, showPicker=false, value, onClose } = _props;
const [selectedValue, setSelectedValue] = React.useState(value)
const inputValue = value;
// onDonePress is for IOS only api
// don't support onValueChange for in this component, for android, pass: onDoneValueCallback instead
// in order to prive a consistent api call for android and ios

React.useEffect(() => {
if (showPicker){
openPicker();
}else {
closePicker();
}
}, [showPicker])

const onValueChangeWrapper = (value:any, index: number) => {
setSelectedValue(value);
setSelectedIndex(index);
if(Platform.OS === 'android' && onDoneValueCallback){
onDonePressWrapper();
}
}

const onDonePressWrapper = () => {
if(onDoneValueCallback){
onDoneValueCallback(selectedValue, selectedIndex);
}
if (onDonePress){
onDonePress();
}
}

const openPicker = () => {
if (pickerRef.current){
if (Platform.OS === 'android') {
pickerRef.current.focus();
} else {
pickerRef.current.togglePicker(true)
}
}
}

const closePicker = () => {
if (pickerRef.current){
if (Platform.OS === 'android') {
pickerRef.current.blur();
} else {
if (pickerRef.current.state.showPicker)
pickerRef.current.togglePicker(false);
}
}
}

const onCloseWrapper = (donePressed: boolean) => {
if (!donePressed && inputValue !== selectedValue){
setSelectedValue(inputValue);
}
if (onClose){
onClose(donePressed);
}
}

return (
<RNPickerSelect
{..._props}
value={selectedValue}
onValueChange={(value, index) => onValueChangeWrapper(value, index)}
ref={Platform.OS === 'ios' ? pickerRef : null}
pickerProps={{ ref: Platform.OS === 'android' ? pickerRef : null }}
style={pickerSelectStyles}
useNativeAndroidPickerStyle={false}
onDonePress={ onDonePressWrapper }
onDownArrow={()=>null}
onClose={onCloseWrapper}
Icon={() => {
return ;
}}
/>
)
}
`

And the usage of this new wrapped component:
<PickerSelect
showPicker={isShowDaysInterval}
value={daysInterval}
items={ BizType.getDaysIntervalItems() }
onDoneValueCallback={(value) => setDaysInterval(value)}
onClose={() => setIsShowDaysInterval(false)}
/>

Additional details

Add any other details or screenshots about the feature request here.

@saaspeter saaspeter changed the title onValueChange in IOS may lead to strange behaviors onValueChange in IOS shouldn't be triggered when only scroll item list May 25, 2024
@saaspeter saaspeter changed the title onValueChange in IOS shouldn't be triggered when only scroll item list in IOS, onValueChange shouldn't be triggered when only scroll item list May 25, 2024
@Jakemangan
Copy link

Jakemangan commented May 30, 2024

This is a massive issue with the iOS implementation. As soon as I press done the value goes back to being the default value if it is set with the value prop on the RNPickerSelect component.

This doesn't happen if value is unset. Android works fine.

With the below I can't select anything aside from "single"

<RNPickerSelect
                useNativeAndroidPickerStyle
                value="single"
                onValueChange={(value) => blockType.set(value)}
                items={[
                    { label: "Single", value: "single" },
                    { label: "Superset", value: "multiSet" },
                    { label: "Dropset", value: "dropSet" },
                ]}
            />

@saaspeter
Copy link
Author

yes, only IOS has this problem. So we need a variable to store and reflect the changed value when the user scroll the item list (if I do not use the variable for it, the item will go back the old value), but I don't want use this variable directly because the user still didn't press 'Done' button, maybe the user just wants to scroll items list to review these items. So what I can think of is: to add a new variable to store and reflect the temporary value, and when the user press 'Done', I will send the final value to the callback function.

But I wonder if we can change the behavior in the component, so I don't need wrap it by myself.

@fbele
Copy link

fbele commented Jul 10, 2024

I agree with this issue. It is really not a desired behavior that the selected value gets updated before pressing on "Done" button or closing of the picker. And what I find strange is, even if you just console.log the value in onValueChange() method, the selected value still gets picked. If that is true, then why is onValueChange() required in the first place? It's like that this updating of the value is happening somehow in the module itself.

@orcuntuna
Copy link

Is there no update on this issue? The value should only change when the done button is clicked

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants