alchemist
alchemist

Reputation: 458

React.js confuse with 'two children with the same key'

I'm using react to mounting two same components on a page,

var SelectBox = React.createClass({
  getDefaultProps: function() {
    return {
      value: []
    };
  },
  getInitialState: function() {
    return {checked: this.props.value,count: 1};
  },
  handleClick: function() {
    var opt = this.state.checked;
    opt.push({id: this.state.count, name: this.state.count});
    this.setState({checked: opt, count: this.state.count + 1});
  },
  render: function() {
    var that = this;
    var options = this.state.checked.map(item => <option key={'checked-' + that.props.name + item.id} value={item.id}>{item.name}</option>);
    return (
      <div>
        <select multiple={true}>
          {options}
        </select>
        <button type="button" onClick={this.handleClick}>add</button>
      </div>
    );
  }
});
React.render(
  <SelectBox name='one'/>,
  document.getElementById('one')
);
React.render(
  <SelectBox name='two'/>,
  document.getElementById('two')
);

then click the button of the 'one', it's alright, but when i click the button of the 'two', some of 'one' crop up in 'two',why?? it make me confuse. console show:

Warning: flattenChildren(...): Encountered two children with the same key, `.$checked-two1`. Child keys must be unique; when two children share a key, only the first child will be used.

but just do some change

var a = [{id: 5, name: 5}];
React.render(
  <SelectBox name='one' value={a}/>,
  document.getElementById('one')
);

it work properly. what happenned?is there something wrong or it's bug?

Upvotes: 3

Views: 12505

Answers (3)

svnm
svnm

Reputation: 24308

This problem seems to come up for me when I set the keys in a list style react component, and the id's passed into the keys are not unique, as the error description suggests.

e.g. 2 of the list items have a key of 1.

This is usually due to an error in the logic of setting up your unique id's.

For my example I was using a redux store for the applications state, and I was setting the id of the item to be items.length + 1 which was incorrect.

This caused the two children with the same key error above for me. To fix it, I set each new item's id to be the number of itemsAdded to the list over time.

It is similar to keys in a database where the id keeps incrementing, so you can have no items in the database due to deleting them all, however your id for the next item could be 1000.

Upvotes: 1

alchemist
alchemist

Reputation: 458

Oh,I find the real reason,getDefaultProps is called once and cached, the any complex objects returned by getDefaultProps() will be shared across instances, not copied, so all SelectBox components without an explicit value prop passed will share the same checked array reference in state. in the case, i should write:

  getInitialState: function() {
    var tmp = this.props.value.concat();
    return {checked: tmp, count: 1};
  },

Upvotes: 4

Henrik Andersson
Henrik Andersson

Reputation: 47172

No, there is no bug here, you are rendering the same instance of the component twice which means that the 'components' share the same state but when you pass along different props the component now gets two states to keep track of.

Upvotes: 3

Related Questions