React-native-picker-select: Placeholder does not seem to update even if objects are different

Created on 10 May 2019  Â·  16Comments  Â·  Source: lawnstarter/react-native-picker-select

YOU MUST FILL OUT THE ENTIRE BUG REPORT

Describe the bug

I seem to be having issues with updating the placeholder after the component is mounted.

To Reproduce

Steps to reproduce the behavior:

  1. Have a managed loading state
  2. Set input to be loading
  3. Have a ternary to switch between placeholders
  4. See error

Expected behavior

The placeholder should update to the _loading_ placeholder and back to the given placeholder

Smartphone (please complete the following information):

  • Device: iPad Mini
  • OS: iOS 12.2
  • react-native-picker-select version: ^6.1.0
  • react-native version: https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz
  • react version: 16.5.0

Reproduction and/or code sample

Investigation:
export default class RNPickerSelect extends PureComponent {
    // <snip>

    static getDerivedStateFromProps(nextProps, prevState) {
        // <snip>

        // update selectedItem if value prop is defined and differs from currently selected item
        const { selectedItem, idx } = RNPickerSelect.getSelectedItem({
            items,
            key: nextProps.itemKey,
            value: nextProps.value,
        });

        const selectedItemChanged =
            !isEqual(nextProps.value, undefined) && !isEqual(prevState.selectedItem, selectedItem);

        console.log(selectedItem, selectedItemChanged) // <-- Logging here

        if (itemsChanged || selectedItemChanged) {
            if (selectedItemChanged) {
                nextProps.onValueChange(selectedItem.value, idx);
            }

            return {
                ...(itemsChanged ? { items } : {}),
                ...(selectedItemChanged ? { selectedItem } : {}),
            };
        }

        return null;
    }

    // <snip>
}
Output:
Object {
  "color": "#9EA0A4",
  "label": "Select an item...",
  "value": null,
} false
Object {
  "label": "Loading...",
  "value": null,
} false
JSX:
<Select
    {...props}
    placeholder={(
        this.isLoading(name)
            ? {
                label: 'Loading...',
                value: null,
            }
            : null
    )}
    disabled={disabled}
    value={this.getValue(name)}
    items={options}
    style={{
        inputIOS: newStyle,
        inputAndroid: newStyle,
    }}
    onValueChange={text => call([this.onChange, onChange], { field, text })}
/>
bug

Most helpful comment

Set the placeholder like this

placeholder={
{ label: 'Select Gender', value: null, color: '#909090', }
}

It works for me.

All 16 comments

After investigating the issue it seems to be down to the input not having a string value. But once the placeholder updates (value of null) then it fires an onValueChange that then clears the string value.

I think there should be a prop or similar to disable resetting of the value if the new selected item's value is null.

Why not just have the ternary switch between an empty string and “Loading”?

On mobile and just taking a quick glance at this - so I may have missed something.

Why not just have the ternary switch between an empty string and “Loading”?

I wasn't aware that it should just be a string? I assume it'd still have the same issue though.

The placeholder prop expects an empty object or an object in the proper format. You’re passing in null instead - not sure how that’s going to react.

The placeholder prop expects an empty object or an object in the proper format. You’re passing in null instead - not sure how that’s going to react.

Interesting. I'm passing in the value null as seen by the default placeholder. I can change that.

i mean

placeholder="{}"

or

placeholder="{ label: '', value: null }"

are correct.

placeholder="null"

^ this may break something. Also - can you fill out the entire bug report? You left off the versions.

placeholder="null"

The placeholder of _null_ becomes placeholder={null} which I assume is the same as not passing anything. As mentioned the _null_ is just to show that there's no pointers between the changing placeholder.

placeholder="{ label: '', value: null }"

This is what is currently being done.


I'll update the report to include versions.

{
    "main": "node_modules/expo/AppEntry.js",
    "scripts": {
        "start": "expo start",
        "android": "expo start --android",
        "ios": "expo start --ios",
        "eject": "expo eject",
        "postinstall": "node ./quiet-console.js"
    },
    "dependencies": {
        "@expo/vector-icons": "latest",
        "@ninetynine/call": "^1.1.0",
        "@ninetynine/noop": "^1.1.1",
        "expo": "^32.0.0",
        "lodash": "^4.17.11",
        "prop-types": "latest",
        "react": "16.5.0",
        "react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz",
        "react-native-datepicker": "^1.7.2",
        "react-native-picker-select": "^6.1.0",
        "react-navigation": "^3.11.0",
        "react-navigation-header-buttons": "^2.3.1",
        "sentry-expo": "^1.12.0"
    },
    "devDependencies": {
        "babel-preset-expo": "^5.0.0"
    },
    "private": true
}

Seemingly setting placeholder as a string crashes the app.

placeholder="Text"

I’m sure it does - it expects an object.

So the ternary of “Loading” to “” didn’t work?

I'll look into this and get back to you

Related to #112 - placeholder won't update because it's the currently selected value and selectedItemChanged doesn't fire in this instance. Would be fixed if this is made into a controlled component - or could potentially be specialcased.

Warning: value prop on select should not be null. Consider using an empty string to clear the component or undefined for uncontrolled components.

Set the placeholder like this

placeholder={
{ label: 'Select Gender', value: null, color: '#909090', }
}

It works for me.

Same bug here
When i reset the select it still remember the old value:

<RNPickerSelect
     key="csp"
     onValueChange={(value): void => SetSalePointsFilter({ key: 'csp', value })}
     Icon={(): React.ReactNode => <Unicons name="angle-down" size={22} />}
     useNativeAndroidPickerStyle={false}
     placeholder={{
          label: 'CSP',
          value: null,
          ...styles.placeholderColor,
      }}
      value={listing.filters.csp}
      style={styles.select}
      items={items.csp}
 />

Same bug here
When i reset the select it still remember the old value:

<RNPickerSelect
     key="csp"
     onValueChange={(value): void => SetSalePointsFilter({ key: 'csp', value })}
     Icon={(): React.ReactNode => <Unicons name="angle-down" size={22} />}
     useNativeAndroidPickerStyle={false}
     placeholder={{
          label: 'CSP',
          value: null,
          ...styles.placeholderColor,
      }}
      value={listing.filters.csp}
      style={styles.select}
      items={items.csp}
 />

Ok, it works for me.
To reset the select, set the component value to null and the value of placeholder also null

<RNPickerSelect
     key="csp"
     onValueChange={(value): void => SetSalePointsFilter({ key: 'csp', value: value || null })}
     Icon={(): React.ReactNode => <Unicons name="angle-down" size={22} />}
     useNativeAndroidPickerStyle={false}
     placeholder={{
          label: 'CSP',
          value: null,
          ...styles.placeholderColor,
      }}
      value={listing.filters.csp}
      style={styles.select}
      items={items.csp}
 />

In my case, placeholder was not getting updated if it is based on state, I solved this using a hack.
In index.js file of the react-native-picker-select
Change following line
const selectedItemChanged = !isEqual(nextProps.value, undefined) && !isEqual(prevState.selectedItem, selectedItem);
to
const selectedItemChanged = !isEqual(nextProps.value, undefined) || !isEqual(prevState.selectedItem, selectedItem) || prevState.selectedItem.valued != selectedItem.value ;

Its a hack but it works.

Was this page helpful?
0 / 5 - 0 ratings