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

onValueChange Event triggers every time render update #112

Open
indrsidhu opened this issue Nov 18, 2018 · 66 comments
Open

onValueChange Event triggers every time render update #112

indrsidhu opened this issue Nov 18, 2018 · 66 comments
Labels
bug Something isn't working

Comments

@indrsidhu
Copy link

indrsidhu commented Nov 18, 2018

Describe the bug
Picker has function prop "onValueChange" as per expectation it should work like click event, when ever someone select value, but i found this event execute all the time when render() updates, so it creating problem, because it null my state value.

To Reproduce
onValueChange = { (value) => console.log("Event Triggered"+value)}
Monitor console, it will execute all the time whenever render() update by any state or prop change.

Expected behavior
It should execute only when we change dropdown value, after pick , not all the time

Smartphone (please complete the following information):

  • Device: OnePlus2
  • OS: Android
  • react-native-picker-select version: 5.1.10
  • react-native version: 0.55.4
  • react version: 16.3.1
    Reproduction and/or code sample
        <PickerSelect
        value={formData['country']}
        placeholder={{label: I18n.t('select'), value: ''}}
        style={pickerStyle}
        onValueChange={(itemValue,itemIndex) => {
			console.log("Country index="+itemIndex);
			console.log("Country current="+formData['country']);
			console.log("Country Changed="+itemValue);
			this.handleChangeValue(itemValue,"country","picker")
		}}
        items={COUNTRY_RENDER}
        />

Once select country i set in state using "handleChangeValue"
formData['country']
Whenever i change language it update my render()
I tried to debug

			console.log("Country index="+itemIndex);
			console.log("Country current="+formData['country']);
			console.log("Country Changed="+itemValue);
			this.handleChangeValue(itemValue,"country","picker")

formData['country']
always return my selected country, but
console.log("Country Changed="+itemValue);

does not return selected index once, i change language.

If language value is same for both english and spanish language, then it should return same index

@lfkwtz
Copy link
Collaborator

lfkwtz commented Nov 18, 2018

Please update your bug report with the missing information

@indrsidhu
Copy link
Author

I updated my post

@lfkwtz
Copy link
Collaborator

lfkwtz commented Nov 18, 2018

Thanks. I’ll take a look. I assume you meant version 5.1.0?

@indrsidhu
Copy link
Author

yes i have latest version

@vikasdosi
Copy link

vikasdosi commented Nov 27, 2018

Describe the bug
Picker has function prop "onValueChange" as per expectation it should work like click event, when ever someone select value, but i found this event execute all the time when render() updates, so it creating problem, because it null my state value.

To Reproduce
onValueChange = { (value) => console.log("Event Triggered"+value)}
Monitor console, it will execute all the time whenever render() update by any state or prop change.

Expected behavior
It should execute only when we change dropdown value, after pick , not all the time

Smartphone (please complete the following information):

  • Device: OnePlus2
  • OS: Android
  • react-native-picker-select version: 5.1.10
  • react-native version: 0.55.4
  • react version: 16.3.1
    Reproduction and/or code sample
        <PickerSelect
        value={formData['country']}
        placeholder={{label: I18n.t('select'), value: ''}}
        style={pickerStyle}
        onValueChange={(itemValue,itemIndex) => {
			console.log("Country index="+itemIndex);
			console.log("Country current="+formData['country']);
			console.log("Country Changed="+itemValue);
			this.handleChangeValue(itemValue,"country","picker")
		}}
        items={COUNTRY_RENDER}
        />

Once select country i set in state using "handleChangeValue"
formData['country']
Whenever i change language it update my render()
I tried to debug

			console.log("Country index="+itemIndex);
			console.log("Country current="+formData['country']);
			console.log("Country Changed="+itemValue);
			this.handleChangeValue(itemValue,"country","picker")

formData['country']
always return my selected country, but
console.log("Country Changed="+itemValue);

does not return selected index once, i change language.

If language value is same for both english and spanish language, then it should return same index

you can solve by using if condition in on value change

like this

  onValueChange={(value) => {
 if(itemValue!=this.state.PickerValueHolder){
this.setState({
                          PickerValueHolder: value,
                        });
                   //your function 
}
}
}

@AndrewAi
Copy link

AndrewAi commented Dec 7, 2018

Hi, When I change the value, the onValuechange fires, but its delayed and the value returned is always one behind. i.e the default value is 'Select a Category' the other values are Music, Poetry, Comedy.
when I pick Music and the onValueChange fires the first time it returns "null" , when I pick Poetry it returns "Music". so everytime its one behind, any ideas whas going on am I doing something wrong

                            <RNPickerSelect
                                placeholder={{
                                    label: 'Select a Category...',
                                    value: null,
                                }}
                                items={this.state.items}
                                onValueChange={(value) => this.setEventCategory(value)}

                                style={{...pickerSelectStyles}}
                                value={this.state.eventCategory}
                                ref={(el) => {
                                    this.inputRefs.picker = el;
                                }}
                            />

Thank you for any help and thank you for RNPickerSelect

@lfkwtz
Copy link
Collaborator

lfkwtz commented Dec 9, 2018

@indrsidhu please create an example on snack.expo.io or share a github project with your issue -- I have tried to reproduce and I'm not having that issue (https://snack.expo.io/S1O1Q5914)

@AndrewAi please create an example on snack.expo.io or share a github project with your issue --- nothing looks wrong in what you've shared (although I don't know what setEventCategory does.

@vikasdosi
Copy link

Hi, When I change the value, the onValuechange fires, but its delayed and the value returned is always one behind. i.e the default value is 'Select a Category' the other values are Music, Poetry, Comedy.
when I pick Music and the onValueChange fires the first time it returns "null" , when I pick Poetry it returns "Music". so everytime its one behind, any ideas whas going on am I doing something wrong

                            <RNPickerSelect
                                placeholder={{
                                    label: 'Select a Category...',
                                    value: null,
                                }}
                                items={this.state.items}
                                onValueChange={(value) => this.setEventCategory(value)}

                                style={{...pickerSelectStyles}}
                                value={this.state.eventCategory}
                                ref={(el) => {
                                    this.inputRefs.picker = el;
                                }}
                            />

Thank you for any help and thank you for RNPickerSelect

Yes i think you are setting state and then calling some
Api
Will suggest you should use like that
this.setstate({“your state same”:value},()=>{console.log(this.state.yourstatename)})

@AndrewAi
Copy link

AndrewAi commented Dec 9, 2018

Hi thank you for your comments. this is my setEventCategory function setEventCategory(eventCategory){ this.setState({eventCategory: eventCategory}); console.log('setEventCategory: eventCategory: ', this.state.eventCategory) }
very simple. should I be using some onSubmit or onDonePress instead of onValueChange ? Thanks very much

@vikasdosi
Copy link

Hi thank you for your comments. this is my setEventCategory function setEventCategory(eventCategory){ this.setState({eventCategory: eventCategory}); console.log('setEventCategory: eventCategory: ', this.state.eventCategory) }
very simple. should I be using some onSubmit or onDonePress instead of onValueChange ? Thanks very much

Try this way this will work
setEventCategory(eventCategory){ this.setState({eventCategory: eventCategory},()=>{console.log('setEventCategory: eventCategory: ', this.state.eventCategory) })}

@AndrewAi
Copy link

AndrewAi commented Dec 9, 2018

Ok I will thank you, but I wont be able to try it till tomorrow. thanks

@lfkwtz lfkwtz added the question Further information is requested label Dec 11, 2018
@glenne
Copy link

glenne commented Dec 22, 2018

I'm seeing the same issue but am unable to reproduce via snack.expo.io. I observed the following sequence:

  1. Picker initialized with an initial value, say 'one'
  2. Callback occurs via onValueChange and setState invoked with new value, say 'two'
  3. getDerivedStateFromProps called in RNPickerSelect. The nextProps has the prior select value of 'one', state has the value of 'two'.
  4. Logic decides that selected state has changed and onValueChanged called again with the nextProps value of 'one' which is the 'old' value. onValueChanged queues up another setState switching the value back to 'one',
  5. The parent finally invokes render for the first time with the state of 'two'
  6. The infinite loop begins switching between the two states.

I worked around this by removing the call nextProps.onValueChange(selectedItem.value, idx); from getDerivedStateFromProps.

@jpandl19
Copy link

jpandl19 commented Jan 7, 2019

I experienced the exact same issue as @glenne. Removing nextProps.onValueChange(selectedItem.value, idx) from getDerivedStateFromProps also fixed the problem for me.

@lfkwtz
Copy link
Collaborator

lfkwtz commented Jan 9, 2019

@jpandl19 @glenne i'm going to release a version with a prop to disable that. should resolve the issue for now and then we can look into it further in the future.

@glenne
Copy link

glenne commented Jan 10, 2019

I fixed mine by turning it into a controlled component like most of the other react-native components so rendering is only driven by props with not state memory. This actually simplifies a number of interactions.

onValueChange(value, index) {
    const { onValueChange } = this.props;
    onValueChange(value, index);
  }

static getDerivedStateFromProps(nextProps, prevState) {
     const newItems = RNPickerSelect.handlePlaceholder({
      placeholder: nextProps.placeholder
    }).concat(nextProps.items);
    const { selectedItem, idx } = RNPickerSelect.getSelectedItem({
      items: newItems,
      key: nextProps.itemKey,
      value: nextProps.value
    });
    return {
      items: newItems,
      selectedItem: selectedItem
    };
  }

EyMaddis added a commit to simpleTechs/react-native-picker-select that referenced this issue Jan 11, 2019
@lfkwtz
Copy link
Collaborator

lfkwtz commented Jan 17, 2019

@glenne @EyMaddis @jpandl19 would you mind checking if this branch solves the issue you were having?

i'd ideally like to make as few breaking changes as possible, so this change will just prevent the internal selectedValue state change from occurring after an onValueChange call IF you have the value prop defined. that way, it will be closer to the controlled component pattern.

@lfkwtz
Copy link
Collaborator

lfkwtz commented Feb 5, 2019

@santanapaulo does this branch solve your issue?

@santanapaulo
Copy link

santanapaulo commented Feb 5, 2019

@santanapaulo does this branch solve your issue?

No @lfkwtz , It didn't work. I installed the version at that branch you said, but no happy ending :/

@codazzo
Copy link

codazzo commented Feb 15, 2019

I was experiencing this in the iOS simulator. Disabling Hot Reloading from the Inspector seems to have fixed this in my case.

@YoshiYo
Copy link

YoshiYo commented Mar 13, 2019

I don't know why, but I fixed the extra render when I removed the props. itemKey.

@lfkwtz
Copy link
Collaborator

lfkwtz commented Mar 13, 2019

Happy to look into this further if someone can provide a working demo that clearly identifies the issue

@altyaper
Copy link

It happened for me whenever I setState inside a promise on onValueChange:

Example:

import PickerSelect from 'react-native-picker-select';
import { View } from 'react-native';

class MyComponent extends Component {
    
   constructor(props) {
      super(props);
      this.state = {
         state: null,
         states: [{ label: 'Arizona', value: 1}, { label: 'California', value: 2}]
      }
   }
  
   handleStateChange(state) {
       // This is a promise
       StateApi.getStates().then(statesResponse => {
            console.log("Logger");
            this.setState(state);
       }); 
   }

  render() {
    <View>
       <PickerSelect
              items={states}
              onValueChange={this.handleStateChange}
              value={this.state.state}
              ref={(el) => {
                  this.inputRefs.state = el;
              }}
          />
    </View>
  }
}

Whenever I change the value of the picker one time, the function handleStateChange is triggered twice.

image

@shanekoss
Copy link

@EyMaddis fork fixed this issue for me.

simpleTechs@e70f4b3

Could this get merged?

My problem was anytime the value was changed elsewhere, the callback was still called.

Fixed for now.

@Achilles718611
Copy link

Achilles718611 commented May 23, 2019

image

How about change order of two lines?

Or we can fire onValueChange after state is updated like followings.
this.setState({
selectedItem: this.state.items[index],
}, () => onValueChange(value, index));

@TheAdamGalloway
Copy link

Thank @sarcadass - this worked for me!

@SmirnovM91
Copy link

Hey everyone, same issue

@SmirnovM91
Copy link

SmirnovM91 commented Jun 7, 2020

I've fixed it for me by adding key parametr to RNPickerSelect like this
image

P.S. I use Mobx state management

@vreuling-bcs
Copy link

I'm having the same issue. (only on Android)
I really hope to see a solution soon!

But since the issue has been unresolved for over 1.5 years now, I might need to consider other options.
Sad though, because other than this issue, this is a really great component!

@lfkwtz Is there any way I can help resolve the issue?

My reproduction snack:
https://snack.expo.io/@bcsbv/rnpickerselect-issue-reproduction

@lfkwtz
Copy link
Collaborator

lfkwtz commented Sep 28, 2020

#368

feedback re: this PR would be appreciated

@jlampel
Copy link

jlampel commented Oct 7, 2020

This was driving me nuts and the above hack by @sarcadass worked for me in the meantime!

@lfkwtz
Copy link
Collaborator

lfkwtz commented Oct 20, 2020

@jlampel mind testing #368?

@lfkwtz
Copy link
Collaborator

lfkwtz commented Nov 4, 2020

@vreuling-bcs can you check #368?

@jlampel
Copy link

jlampel commented Nov 4, 2020

Ok, apologies for the delay but I tested #368 and unfortunately it did not solve this particular issue - the picker continued flickering back and forth between the old and new value, even with the above workaround in place.

@lfkwtz
Copy link
Collaborator

lfkwtz commented Nov 17, 2020

@jlampel can you provide a repro?

@lfkwtz
Copy link
Collaborator

lfkwtz commented Dec 3, 2020

would like to finally close this out - if someone could please provide a full repro of this issue on v8.0.4 or higher that would be appreciated

@marcolangebeeke
Copy link

I've fixed it for me by adding key parametr to RNPickerSelect like this
image

P.S. I use Mobx state management

I tried all other solutions, but finally this did the job for me! Thanks @SmirnovM91

Set the key prop to the same state value as the value prop.. how weird!

...
key={this.state.country}
value={this.state.country}
...

Had the issue only on Android, using:
"@react-native-picker/picker": "^1.9.8"
"react-native-navigation": "^7.0.0"

@NathanBeesley
Copy link

I am experiencing this as well with latest.

"expo": "^39.0.0"
"react": "16.13.1"
"react-native": "https://github.com/expo/react-native/archive/sdk-39.0.3.tar.gz"
"@react-navigation/native": "^5.9.3"
"@react-navigation/stack": "^5.14.3"
"react-native-picker-select": "^8.0.4"
iOS 14.4

`

import React, { FunctionComponent, useRef, useState } from 'react'
import { StyleSheet, View, Text } from 'react-native'
import { Container } from '../../component/src/container'
import RNPickerSelect, { Item } from 'react-native-picker-select'
import * as style from '../../style'
import { Ionicons } from '@expo/vector-icons'
import { iconOS } from '../../lib/src/util'

const _style = StyleSheet.create({
inputIOS: {
...style.form.input,
paddingRight: 30
},
inputAndroid: {
...style.form.input,
paddingRight: 30
}
})

interface State {
selectedItem?: Item
items?: Array
}

export const TestComponent: FunctionComponent = (props) => {

const [state, setState] = useState<State>({
    selectedItem: null,
    items: [
        {
            key: 'lbl_1',
            label: 'Label 1',
            value: 0
        },
        {
            key: 'lbl_2',
            label: 'Label 2',
            value: 1
        },
        {
            key: 'lbl_3',
            label: 'Label 3',
            value: 2
        }
    ]
})

const onValueChange = (value: any, index: number) => {
    console.log('onValueChange: ', value) // Called Twice
    setState({ ...state, selectedItem: value })
}

const renderIcon = () => {
    return <Ionicons name={iconOS + '-arrow-dropdown'} size={20} style={style.form.selectIcon} />
}

return (
    <Container style={{ flex: 1, paddingHorizontal: 16 }}>
        <View style={{ ...style.form.formGroup, marginBottom: 16 }}>
            <View style={[style.form.inputContainer ]} >
                <RNPickerSelect
                    items={state.items}
                    value={state.selectedItem}
                    placeholder={'Test'}
                    useNativeAndroidPickerStyle={false}
                    style={_style}
                    Icon={renderIcon}
                    onValueChange={onValueChange} />
            </View>
        </View>
    </Container>
)

}
`

@migupry
Copy link

migupry commented May 4, 2021

Using [email protected], [email protected].

Can confirm the following works without the issue:

<RNPickerSelect
        value={pickerValue}
        key={pickerValue}
        onValueChange={value => {
          console.log(value), setPickerValue(value);
        }}
        items={pickerArray}
      />

As soon as I remove the key property, i get multiple console.logs (multiple re-renders) as I change values, so I can confirm the key property fix the issue.

PS: Using functional components, and useState hook for pickerValue,setPickerValue

@tq-bao
Copy link

tq-bao commented May 27, 2021

You should use with useEffect. This's my solution:

useEffect(() => { // call function }, [value])
<RNPickerSelect value={value} onValueChange={setValue} items={pickerArray} />

@Zygro
Copy link

Zygro commented Jul 5, 2021

Using [email protected], [email protected].

Can confirm the following works without the issue:

<RNPickerSelect
        value={pickerValue}
        key={pickerValue}
        onValueChange={value => {
          console.log(value), setPickerValue(value);
        }}
        items={pickerArray}
      />

As soon as I remove the key property, i get multiple console.logs (multiple re-renders) as I change values, so I can confirm the key property fix the issue.

PS: Using functional components, and useState hook for pickerValue,setPickerValue

This introduces another problem on iOS, the picker then closes automatically on setteling on a value, not allowing for continuous scrolling.

@lindskogen
Copy link

Just adding my experience here if it helps someone:

I'm trying to make the Picker only emit its value when Done is pressed. To do this I stored the index of the selected item and call onValueChange in onDone instead. My issue was the placeholder item this library inserts at the front of the items, this caused my onValueChange to be called repeatedly, iterating through the items array.

The solution for me was to set the placeholder prop to an empty object. This disables the initial "label" that is shown at the top and prevents this infinite renders issue.

Another solution for this would probably be to emit an onChange event for index-1 instead if the placeholder is present.

const selectedItem = items[index] as Item | undefined;

return (
  <PickerSelect
      placeholder={{}} // <- this fixed my issue
      items={items}
      value={selectedItem?.value ?? null}
      onValueChange={(v, i) => setIndex(i)}
      onDonePress={() => {
        if (selectedItem) {
          onValueChange(selectedItem.value, index);
        }
      }}
  />
);

@girish54321
Copy link

Thanks to @vikasdosi comment
I don't why this is not fixed this bug is opened on 2018 🤔 🤔

@AntonyM71
Copy link

Thanks to @vikasdosi comment
I don't why this is not fixed this bug is opened on 2018 🤔 🤔

Seemingly because someone derailed this thread with a completely separate issue. I don't understand why such a critical issue hasn't been resolved after all this time either.

@saga95
Copy link

saga95 commented Jul 7, 2022

I fixed this issue using the value prop

             <SelectPicker
                value={client}
                placeholder="Smashtaps PVT"
                onChange={value => setClient(value)}
                data={customerDataList}
              />

@WiharlleyWill
Copy link

WiharlleyWill commented Aug 5, 2022

Hi, I solved this problem in another way.
Start your value with null, and just render after the "default" value is loaded.

{myValue &&
<SelectPicker
value={myValue}
placeholder="Smashtaps PVT"
onChange={value => setClient(value)}
data={customerDataList}
/>
}

@caiogomesdev
Copy link

version: "react-native-picker-select": "^8.0.4";
I fixed this problem like this:

const [valuePicker, setValuePicker] = useState('');
return (
<PickerSelect
placeholder={{label: 'some text', value: '' }}
items={items}
value={valuePicker}
onValueChange={(v) =>{
if (v !== valuePicker){
setValuePicker(v);
}
}}
/>
)

@AtypicalSysAdmin
Copy link

If you want a very cheap, shameful and dirty solution (but working nonetheless). You can validate that the value has been changed by the user if the value has been changed x milliseconds (in that case 100 ms) after the component has been mounted (because the user won't have enough time to click the element and select a value in such a short time). I tested it and it's working on Android, iOS and web. You should however wash your hands after using that hack 😬 :

const Component = ({ ... }) => {
  const defaultValue = 'defaultValue'
  let mountedOn

  useEffect(() => {
    mountedOn = Date.now()
  })

  return (
    <RNPickerSelect
      ...
      value={defaultValue}
      onValueChange={(value) => {
        if (value !== defaultValue && Date.now() - mountedOn > 100) {
          dispatchFunction(value)
        }
      }}
    />
  )
}

You're a genius, thanks:)

@brax10ward
Copy link

Checking to see if there are any fixes for this bug?

@notSeiti
Copy link

notSeiti commented Mar 22, 2024

If anyone is still having this issue, I'd like to add my two cents. I was having the same issue and somehow I solved it by changing the type of value in the items list.
Using value as a number was triggering the render for me but, when I changed value to string, the problem stopped.

items={[
{ label: "option 1", value: "USE STRING INSTEAD OF NUMBER" },
]}

Hope it helps

@Nonoyamalfoy
Copy link

Nonoyamalfoy commented Jun 20, 2024

The same problem occurs when using "@react-native-picker/[email protected]" or later.
react-native-picker/picker#538

This is related to the following issue, which is caused by the onValueChange param always being a string.
It is derived from the fact that if the type of value of items is set to string, the problem will no longer occur.
It might be better to use string or use itemKey instead of value props.

const [day, setDay] = useState<number | null>(null);
...
<RNPickerSelect
  items={[
    {
      label: '1 day',
      value: 1, // number
    },
    {
      label: '2 day',
      value: 2,
    },
  ]}
  value={day}
  onValueChange={(value) => {
    console.log('####value', typeof value); // string
    setDay(Number(value));
  }}
/>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests