React Warning: flattenChildren(...): Encountered two children with the same key
Asked Answered
L

4

26

Could someone please explain how to fix this error

Warning: flattenChildren(...): Encountered two children with the same key

I have replicated my code below, but for some reason CodePen is not showing the error.

var FilterOptions = React.createClass({
changeOption: function(type, e) {
var val = e.target.value;
this.props.changeOption(val, type);
},

render: function() {

return (
  <div className="filter-options">
    <div className="filter-option">
      <select id="product" name="Product" value={this.props.product} onChange={this.changeOption.bind(this, 'product')}>
      <option value=''>Product</option>
      {this.props.productOptions.map(function(option) {
        return (<option key={option}  value={option}>{option}</option>)
      })}
      </select>
  </div>
  </div>
 );
 }
 });

Codepen

As a secondary question, I am pretty sure my reset is supposed to reset the values of the select boxes but this is also not working and just resetting the rendered results - not sure if this is related to the first problem?

Any help much appreciated

Lorolla answered 17/1, 2017 at 17:15 Comment(4)
Are you sure this.props.productOptions has unique values? If so then are you sure this code is giving the error and not somewhere else?Kimon
@MartinMazzaDawson No, there are some duplicate values in all select menus - the exact error are all like these - bundle.js:9899 Warning: flattenChildren(...): Encountered two children with the same key, 1:$prod3. Child keys must be unique; when two children share a key, only the first child will be used.Lorolla
Does the error go away if you change key to be the index value instead of option ?Quarters
Error is self explanatory, you should not have two items with same key. What kind of help you are looking for?Galliard
L
13

Adding the index as value fixed this. Thanks @azium for your sugegstion.

  <select id="product" name="Product" value={this.props.product} onChange={this.changeOption.bind(this, 'product')}>
      <option value=''>Product</option>
      {this.props.productOptions.map(function(option, value) {
        return (<option key={value}  value={option}>{option}</option>)
      })}
      </select>
Lorolla answered 17/1, 2017 at 17:46 Comment(2)
This seems counter-intuitive to what React wants you to do, but yet it works. I was using a unique ID field before and it took me an hour to figure out this solution. I still have no idea why it was rendering multiple items with the same key on a simple mapped array where IDs were definitely unique.Bort
this is a poor answer. In future, if you map() another array, and the index is the same, you'll get a key conflict.Glaring
P
31

It is not a good idea to use the index as the key. A key is the only thing React uses to identify DOM elements. What happens if you push an item to the list or remove something in the middle? If the key is same as before React assumes that the DOM element represents the same component as before. But that is no longer true. From: https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318

It is much better to use a unique string from each item you are mapping over as the key. Something like <option key={value.id}> or if a key does not exist, create a unique identifier by doing something like <option key={value.name + value.description}>.

Pandiculation answered 14/4, 2017 at 16:18 Comment(2)
Arrg but what if there is nothing unique in the props object? In my case I have a row with 5 checkboxes. The checkboxes are generated for each entry in an array. But there is nothing unique about them....Pertussis
@Pertussis Best would be to use immutable data in this case (or at least try not to systematically mutate your checkboxes), and use a hash of the object as the key. That's the only way you can differentiate objects such as points, checkboxes, etc. and create a unique key.Equerry
L
13

Adding the index as value fixed this. Thanks @azium for your sugegstion.

  <select id="product" name="Product" value={this.props.product} onChange={this.changeOption.bind(this, 'product')}>
      <option value=''>Product</option>
      {this.props.productOptions.map(function(option, value) {
        return (<option key={value}  value={option}>{option}</option>)
      })}
      </select>
Lorolla answered 17/1, 2017 at 17:46 Comment(2)
This seems counter-intuitive to what React wants you to do, but yet it works. I was using a unique ID field before and it took me an hour to figure out this solution. I still have no idea why it was rendering multiple items with the same key on a simple mapped array where IDs were definitely unique.Bort
this is a poor answer. In future, if you map() another array, and the index is the same, you'll get a key conflict.Glaring
S
5

I'm a big fan of using key by combining index with some constant value rather than using key={value.name + value.description}:

key={'some-constant-value'+index}

This is because I can pass the key knowingly which compoent is it for. For eg. <ComponentA key={'compoent-a-'+i} />. Also, I follow this approach is because simple html convention matches like we give id="my-some-of-the-id" or something.

So, even if you want to use name and description as the key, you may use like this rather:

key={'some-constant-'+value.name+'-'+value.description}

This is just an opinion. Though, I follow html convention when writing props value.

Southsouthwest answered 7/9, 2018 at 10:20 Comment(0)
E
0

actually you need to specify to each children a unique key,so for that you need to create another key,for example if you are getting data from the database so for that create a new column for example (id) and then add value of that column to your div or what matter you are looping on as a key

var FilterOptions = React.createClass({
changeOption: function(type, e) {
var val = e.target.value;
this.props.changeOption(val, type);
},

render: function() {

return (
  <div className="filter-options">
    <div className="filter-option">
      <select id="product" name="Product" value={this.props.product} onChange={this.changeOption.bind(this, 'product')}>
      <option value=''>Product</option>
      {this.props.productOptions && this.props.productOptions.map(function(option) {
        return (<option key={option.id}  value={option}>{option}</option>)
      })}
      </select>
  </div>
  </div>
 );
 }
 });

i hope this help anyone in the future.

Evenhanded answered 11/10, 2020 at 15:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.