Search code examples
javascriptreact-nativelodash

The same value return different value with lodash


Case 1:

I set specify option value selectedZone to my <DropDownMenu /> it works fine.

<DropDownMenu /> is my selector:

  render() {
    let { zone, selectedZone, selectedCity } = this.state;

    return (
        <DropDownMenu
            style={{
              selectedOption: {
                marginBottom: -5
              }
            }}
            styleName="horizontal"
            options={zone}
            selectedOption={selectedZone || zone[0]}
            onOptionSelected={(zone) => {
                this.setState({ selectedZone: zone, selectedCity: zone.children[0] });
            }}
            titleProperty="brand"
            valueProperty="id"
        />

        ...
     )
   }

Case 2:

When I specify the value selectedZone from my AsyncStorage (The value is the same) it doesn't work and show yellow warnning.

So I try to check the source code.

The function getSelectedOption() from <DropDownMenu /> source code.

  getSelectedOption() {
    const { options, selectedOption } = this.props;
    console.log('check options');
    console.log(options);
    console.log('check selectedOption');
    console.log(selectedOption);
    console.log('check _.indexOf(options, selectedOption');
    console.log(_.indexOf(options, selectedOption));
    if (_.indexOf(options, selectedOption) === -1) {
      console.warn(
        `Invalid \`selectedOption\` ${JSON.stringify(selectedOption)}, ` +
        'DropDownMenu `selectedOption` must be a member of `options`.' +
        'Check that you are using the same reference in both `options` and `selectedOption`.'
      );
      return;
    }
    return selectedOption;
  }

lodash function indexOf will return 1 in Case 1.

enter image description here

rerun -1 in Case 2, I think it should return 1 just like Case 1

enter image description here

I compare with options and selectedOption can't see what is different in Case 1 and Case 2.

Some one can teach me what step I miss it ?

Any help would be appreciated. Thanks in advance.


Solution

  • When I specify the value selectedZone from my AsyncStorage (The value is the same) it doesn't work and show yellow warnning.

    Lodash has very little to do with this - it comes down to how JavaScript compares objects - two of them are the same ONLY if they are literally the same underlying object:

    const a = {name: "fred"};
    const b = a; //literally the same object
    const c = {name: "fred"}; //an object that looks the same
    
    console.log("a, b, c", a, b, c);
    console.log("a === b", a === b); 
    console.log("a === c", a === c);

    Two objects with the same values which are different objects are not considered the same. At least, not according to indexOf, as it uses the JavaScript comparison:

    const arr = [
      {name: "alice"},
      {name: "bob"},
      {name: "carol"}
    ];
    
    const originalObject = arr[1]; //literally the same object
    const newObject = {name: "bob"}; //an object that looks the same
    
    const indexWithOriginalObject = _.indexOf(arr, originalObject);
    const indexWithNewObject = _.indexOf(arr, newObject);
    
    console.log("index when using the original object:", indexWithOriginalObject);
    console.log("index when using a new object that looks the same:", indexWithNewObject);
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

    However, you can use the Lodash method findIndex that will correctly do the comparison.

    const arr = [
      {name: "alice"},
      {name: "bob"},
      {name: "carol"}
    ];
    
    const index = _.findIndex(arr, {name: "bob"});
    
    console.log("index", index);
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

    A word of warning - invoking findIndex with only an object will only check the properties you supplied. So it may not find the exact object you want, if you have more than one that look the same. You can use isEqual in a callback if you want to check that the two objects look EXACTLY the same

    const arr = [
      {name: "alice"},
      {name: "bob", lastName: "bloggs"},
      {name: "bob", lastName: "smith"},
      {name: "carol"}
    ];
    
    const indexPartial1 = _.findIndex(arr, {name: "bob"});
    const indexPartial2 = _.findIndex(arr, {name: "bob", lastName: "smith"});
    
    const indexEqual1 = _.findIndex(arr, x => _.isEqual(x, {name: "bob"}));
    const indexEqual2 = _.findIndex(arr,x => _.isEqual(x, {name: "bob", lastName: "smith"}));
    
    console.log(
      "index by partial equality where the argument only has one property:", 
      indexPartial1
    );
    console.log(
      "index by partial equality where the argument has both properties:", 
      indexPartial2
    );
    
    console.log(
      "index by full equality where the argument only has one property:", 
      indexEqual1
    );
    console.log(
      "index by full equality where the argument has both properties:", 
      indexEqual2
    );
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>