React-native: Android Picker Not Consistently Firing onValueChange

Created on 18 Aug 2017  Â·  61Comments  Â·  Source: facebook/react-native

Is this a bug report?

Yes. I have also seen this issue on stackoverflow.

Have you read the Contributing Guidelines?

Yes

Environment

  1. react-native -v: 2.0.1
  2. node -v: v8.0.0
  3. npm -v: 5.0.0
  4. yarn --version: 0.24.5
  • Target Platform: Android
  • Development Operating System: macOS
  • Build tools: N/A

Steps to Reproduce

Using the Picker component in Android, the first onValueChange fires, but the next one does not. The one after does...this continues and results in about 33% of the time onValueChange does not fire.

I created a snack with a basic example.

Expected Behavior

I expected onValueChange to fire if the Picker item selected is different from the current selection.

Actual Behavior

About 33% of the time onValueChange will not fire, but the text on the Picker does change, which is confusing to the user. The other times, everything works as expected.

Reproducible Demo

I created a snack with a basic example. Select one option, then another and, in my experience, it will not consistently log to console.

Ran Commands

Most helpful comment

I found a solution. I my case i was using redux-form.
Looks like this component must use state and setState.

something like..

Note this lines:
this.setState({ itemValue });
onChange(itemValue);

constructor(props) {
    super(props);
    this.state = { itemValue: this.prop.initialSelectedValue };
  }
render () {
const {
      input: { value, onChange, ...inputProps },
    } = this.props;
return (
       <Picker
          selectedValue={this.state.itemValue}
          onValueChange={itemValue => {
            this.setState({ itemValue });
            onChange(itemValue);
          }}
          {...inputProps}
        >
);
}

Cheers

All 61 comments

I am currently experiencing this error too, with about the same 33% statistic.

I also have exactly the same problem. Any solutions / workarounds?

Unfortunately I have the same problem. Has anyone found any solution to resolve this behavior? Or at least an workaround.

+1 encountering this bug in two separate applications. happens about every other selection

+1 seeing the exact same thing :(. any workarounds to this problem?

+1 Unbelievable that this error occurs. Do I have to try and rewrite my app not using the Picker just so that it will reliable? Crazy world. Any workarounds would be appreciated.

did you try adding a state? demo: https://snack.expo.io/r1JfNfKRZ

Still happening in 0.49. Honestly this Picker component should be removed from the doc or marked as "alpha", as it simply doesn't work and shouldn't be relied on. If I had known from the start that it was broken, I could have used a third party picker or make my own.

Can anyone point to a related commit that might have broken this? I can probably look at this if someone can point me in the right direction

+1
Same problem... in RN 0.48.1

FYI: tested on 'left hand'/'right hand' example above and this appears to be fixed on master; no idea which commit actually fixed it

+1 any workaround for this?

+1 Any temporary workaround around this problem ??

Same problem.
Used to be good until recently.
on react-native 0.47.2 atm.
In what version is this fixed?
(I could not upgrade to latest yet because gradle scripts seem to be broken / missing)
the workaround suggested by sandropoluan does not work for me.

@tombailey any chance you are able to figure out what the commit was so it can be proposed for the next release?

adding setState to onValueChange does not work, onValueChange is not called half of the time, so changing it does not make a difference.

@ThaJay I have looked but I have no idea which commit resolved this. I didn't do an upgrade on my project, I just created a new one with the example above and pointed at the latest version of RN. Can someone else verify it works for them too?

I just made a test project and was unable to reproduce:
https://github.com/ThaJay/react-native-picker-test
It worked in the same version of react-native I have this problem with in my app, so this does not get me closer to figuring it out. Anyway, it's not something obvious.

If I remove state and setState it still seems to call onValueChange with the right value but the selected value in the picker does not change any more.
Funny thing is, when I have this issue in the app, the value of the picker DOES change, even though onValueChange does not seem to get called. (in my app I use redux to store the value)

Further testing learns that the behavior does not change on the latest version (v0.50.4)
I just updated my project and the picker fires onValueChange exactly half the time, while the view element actually updates every time just like on 0.47.2 (but it should not update because onValueChange sets the state it depends on, so it's like the Android picker changes but nothing else).

As stated above I was unable to reproduce this in an empty project but I'm still stunned as of how this can happen and it's a blocking bug for the next release that's waiting to be approved.

The example OP gives does not work, the Picker never changes because it has no selectedValue.
If I change it as below, it works normally.
Perhaps it has something to do with Redux.

import React, {Component} from 'react'
import {Picker, View} from 'react-native'

export default class App extends Component {
  constructor(props) {
    super(props)
    this.state = {hand:'right'}
  }

  render () {
    return (
      <View>
        <Picker
          onValueChange={hand => {
            this.setState({hand})
            console.log('changed to', hand)
          }}
          selectedValue={this.state.hand}
          style={{width:160}}
          mode='dropdown'
        >
          <Picker.Item label='Right Hand' value='right' />
          <Picker.Item label='Left Hand' value='left' />
        </Picker>
      </View>
    )
  }
}

I added redux to the test repo and was still unable to reproduce
https://github.com/ThaJay/react-native-picker-test

@ThaJay given that this is Android specific and seems to happen 50% of the time, I am inclined to blame https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPicker.java#L133

mSuppressNextEvent in particular

I don't have the time to look at this properly so if anyone else does that would be great.

If I disable mSuppressNextEvent in ReactPicker it does seem to work.
It also works if I use state and no redux in our app (but adding redux did not reproduce in the test app).

So there must be something going wrong with the re-render

I have found the folowing workaround using componentDidUpdate:
so I don't have to change react-native

  constructor (props) {
    super(props)
    this.state = {value:this.props.eventId}
  }

  componentDidUpdate () {
    if (
      this.state.value &&
      this.props.eventId !== this.state.value
    ) {
      this.props.chooseEvent(this.state.value)
    }
  }

  onValueChange = value => {
    this.setState({value})
  }

with

          <Picker
            selectedValue={this.state.value}
            onValueChange={this.onValueChange}
          >

This issue still happening until this days... unbelievable!

+1 fix for this. The same issue with Android Picker.

For anyone still having issues with this, you might be interested in https://github.com/tombailey/react-native-fixed-android-picker as a work around

I found a solution. I my case i was using redux-form.
Looks like this component must use state and setState.

something like..

Note this lines:
this.setState({ itemValue });
onChange(itemValue);

constructor(props) {
    super(props);
    this.state = { itemValue: this.prop.initialSelectedValue };
  }
render () {
const {
      input: { value, onChange, ...inputProps },
    } = this.props;
return (
       <Picker
          selectedValue={this.state.itemValue}
          onValueChange={itemValue => {
            this.setState({ itemValue });
            onChange(itemValue);
          }}
          {...inputProps}
        >
);
}

Cheers

I struggled with this problem during several hours and @diegolaciar 's workaround works !

It may be related how the Picker handles the render process, and in which order it updates its internal selected value.
Indeed, it looks like the internal selected value is updated before the check to know if the value has changed or not.

Well, at least we have a workaround now :) Thanks again diego

It seems you should add width property of style to picker in Android. Try adding width and it should work on Android.

@diegolaciar's workaround works!!!!! (We're using redux-form too.)
THANKS. I thought it's the end of the world.

Thanks for posting this! It looks like you may not be using the latest version of React Native, v0.53.0, released on January 2018. Can you make sure this issue can still be reproduced in the latest version?

I am going to close this, but please feel free to open a new issue if you are able to confirm that this is still a problem in v0.53.0 or newer.

How to Contribute • What to Expect from Maintainers

This problem still exist on 0.53.x. Somebody please reopen this issue.

It seems that the above says you'd have to open a new issue. Would you mind doing that @sandropoluan if it's still reproducible?

Can confirm that this bug still exists in 0.53.0

Since this is still happening could the issue be re-opened? Creating a new issue is not ideal since they tend to be auto-closed quickly as less people are watching them.

yes indeed my unwieldy workaround is still in place.

for those still experiencing this issue as we are, you can check https://github.com/sleighdogs/react-native-android-native-picker for a quick workaround.

The base is same as tombailey's solution but we needed to be able to set dialog title and have flexibility with what is displayed as picker component.

I would really appreciate if someone might try to fix this issue.

We have a workaround, but it is not good. For instance it is not working as soon as the component should be able to receive prop updates from Redux.

@diegolaciar I don't understand what the difference between your solution and the normal implementation of Picker, to be honest. Is it the onChange() ? or is that related to redux-form? Because the standard Picker also uses setState(). Not sure what i'm missing.

@AliaMYH Take a look at my example too, both this.state and this.setState are needed to make it work. Anything else does not work. Props do not work.

I working on React Native v0.54.0, this bug still appeared, the action onValueChange on Android does not fire when the user selected the first item on the picker.

I'm on react-native 0.55.0 and the issue is back even with my workaround. What do I do now? :/

@ThaJay until the root cause is determined and fixed, you probably want to try https://github.com/facebook/react-native/issues/15556#issuecomment-360076047

@tombailey I had a similar workaround in place. https://github.com/facebook/react-native/issues/15556#issuecomment-347885494
It stopped working.
So I decided I am done with this picker component and built my own. Easy peasy, some stuff with a
TouchableOpacity and a Modal with a list.

@ThaJay the solution I posted isn't a "workaround" in the same way your's was. It does actually what you propose "TouchableOpacity and a Modal with a list".

@tombailey Oh sorry, I thought you meant @diegolaciar's post below yours. That contains the same workaround.

Your repo is indeed similar to what I built yesterday. It's nice to have control over styling, isn't it? Finally my picker dialog is dark in night mode. :+1:

@hramos , can you reopen this issue?

I have the same issues. I think this issues should be reopen. Thank you very much

I use setTimeout to achieve it, it's a bit tricky but it works

onValueChange={(itemValue, itemIndex) => { setTimeout(() => {this.onValueChange(itemValue, itemIndex)}, 10)}}

Folks, I think it should be used with state instead of props like this.props.value in redux-form.

I use setTimeout to achieve it, it's a bit tricky but it works

onValueChange={(itemValue, itemIndex) => { setTimeout(() => {this.onValueChange(itemValue, itemIndex)}, 10)}}

It helped, thanks.

@hafizalfaza Youu solution worked although i just needed to use:
onValueChange={(itemValue, itemIndex) => { setTimeout(() => {this.onValueChange(itemValue, itemIndex)}, 0)}}

I really don't understand how this works fine but the picker doesn't work well. my react-native version i s 0.57

For people revisiting, @hafizalfaza 's solution is the way to go. Picker's onValueChange needs setTimeout or some other way of adding to event loop to work correctly

Why is this even closed? Issue is still exist, @hafizalfaza 's solutions isn't that I really want to use in my life

Then don't use it. I have had a different Picker component in my app for ages. This is not likely to get fixed any time soon.

edit: LOL 1 day later it got fixed

What do you recommend @ThaJay ?

I made my own. I think there was someone in this thread who made his own and published it as a module, but I just made a gist out of my code as an example of how this could be done.
https://gist.github.com/ThaJay/80ee8647940e0cb753a9da67f55b14df

I just pasted 2 files together and removed duplicate imports so it will not work as is, but maybe you can use parts of it in your own project.

This issue still reproduces. Picker does not trigger onValueChange for first item when there is not selected value. Even if we set selectedValue={this.state.value} where this.state.value = -1 or this.state.value = null it does not trigger onValueChange for first item. So how we can define Picker without any pre-selected value?

Guys, I solved this problem with unselectable first item by setting fake unselectable item with value = -1. Then when user picks valid item,fake item with value = -1 dissapears.

// this.state.selectedIndex initially setted to -1
<Picker
    selectedValue={this.state.selectedIndex}
    onValueChange={(value, index) => { this.setState({selectedIndex: index}) }}
>
    // first fake item
    if(Platform.OS === 'android' && this.state.selectedIndex === -1) {
        <PickerIOS label={'CANCEL'} value={-1} />
    }

    <Picker.Item label={'First'} value={1} />
    <Picker.Item label={'Second'} value={2} />
    <Picker.Item label={'Third'} value={3} />
</Picker>

following code working fine on android but in "onValueChange" this.changeCityArray() not calling on IOS platform please help.....

iosText={this.state.nok_state}
iosTouchStyle={styles.iosPickerStyle}
onPress={() => showActionSheetState()}
onValueChange={(itemValue, itemIndex) => ( this.changeCityArray(itemIndex), this.setState({nok_state: itemValue, key: itemIndex}) )}
render={this.stateOptions} selectedValue={this.state.nok_state} style={styles.pickerStyle}
/>

I use setTimeout to achieve it, it's a bit tricky but it works

onValueChange={(itemValue, itemIndex) => { setTimeout(() => {this.onValueChange(itemValue, itemIndex)}, 10)}}

Great! A little bit tricky but it works xd
Can someone explain why it works?

Was this page helpful?
0 / 5 - 0 ratings