Search code examples
reactjsparentchildren

ReactJS parent/child list items not rendering properly after an item is removed


Example: https://jsfiddle.net/wbellman/ghuw2ers/6/

In an application I am working on, I have a parent container (List, in my example) that contains a list of children (Hero, in my example). The list is governed by an outside object. For simplicity I declared the object directly in the JS. (In my real application the data store is properly namespaced and so forth.)

The problem I have is in the list I have three elements, if I remove an item from the middle, the rendered list appears to remove the last element. However the outside object reflects the proper list.

For example:

  • My list has the elements: cap, thor, hulk
  • If you remove "thor", "cap" and "thor" are rendered
  • The heroList reflects "cap" and "hulk" as it should

I am relatively new to ReactJs, so there is a good chance my premise is fundamentally flawed.

Note: The example reflects a much more complex application. It's structured similarly for purposes of demonstration. I am aware you could make a single component, but it would not be practical in the actual app.

Any help would be appreciated.

Here is the code from JSFiddle:

  var heroList = [
    { name: "cap" },
     { name: "thor"},
    { name: "hulk"}
  ];

  var List = React.createClass({
    getInitialState() {
      console.log("heros", heroList);
      return {
        heros: heroList
      };
    },

    onChange(e){
      this.setState({heros: heroList});
    },

    removeHero(i,heros){
      var hero = heros[i];
      console.log("removing hero...", hero);
       heroList = _.filter(heroList, function(h){ return h.name !== hero.name;});
      this.setState({heros:heroList});
    },

    render() {
      var heros = this.state.heros;

      var createHero = (hero,index) => {
        return <Hero hero={hero} key={index} onRemove={this.removeHero.bind(this,index,heros)}/>;
      };


      console.log("list", heros);

      return (  
        <ul>
          {heros.map(createHero)}
        </ul>
       )
    }
  });

  var Hero = React.createClass({

    getInitialState() {
      return {
        hero: this.props.hero
      }
    },

    render() {
      var hero = this.state.hero;
      return (
        <li>Hello {hero.name} | <button type="button" onClick={this.props.onRemove}>-</button></li>
      );
    }
  });

  ReactDOM.render(
    <List />,
    document.getElementById('container')
  );

Additional: I was having problems copying the code from JSFiddle, anything I broke by accident should work in the JSFiddle listed at the top of this question.

Edit:

Based on the commentary from madox2, nicole, nuway and Damien Leroux, here's what I ended up doing:

https://jsfiddle.net/wbellman/ghuw2ers/10/

I wish there was a way to give everyone credit, you were all a big help.


Solution

  • The problem is with the keys used. Since the key is taken from the index, that key has already been used and thus the hero with that key is shown.

    change it to key={Math.random() * 100} and it will work