Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
202 views
in Technique[技术] by (71.8m points)

javascript - Passing keys to children in React.js

I am running through a react tutorial on tutsplus that is a bit old, and the code doesn't work as it was originally written. I actually am totally ok with this as it forces me to learn more independently, however I have spent a while on a bug that I just can't figure out. The bug consists of not being able to pass on an objects key, which prevents my program from updating the state of the correct object.

First off here is the repo if you want to run this code and see it in action: https://github.com/camerow/react-voteit

I have a child component that looks like this:

var FeedItem = React.createClass({

  vote: function(newCount) {
    console.log("Voting on: ", this.props, " which should have a key associated.");

    this.props.onVote({
      key: this.props.key,
      title: this.props.title,
      description: this.props.desc,
      voteCount: newCount
    });
  },

  voteUp: function() {
    var count = parseInt(this.props.voteCount, 10);
    var newCount = count + 1;
    this.vote(newCount);
  },

  voteDown: function() {
    var count = parseInt(this.props.voteCount, 10);
    var newCount = count - 1;
    this.vote(newCount);
  },

  render: function() {
    var positiveNegativeClassName = this.props.voteCount >= 0 ?
                                    'badge badge-success' :
                                    'badge badge-danger';
    return (
      <li key={this.props.key} className="list-group-item">
        <span className={positiveNegativeClassName}>{this.props.voteCount}</span>
        <h4>{this.props.title}</h4>
        <span>{this.props.desc}</span>
        <span className="pull-right">
          <button id="up" className="btn btn-sm btn-primary" onClick={this.voteUp}>&uarr;</button>
          <button id="down" className="btn btn-sm btn-primary" onClick={this.voteDown}>&darr;</button>
        </span>
      </li>
    );
  }

});

Now when someone hits the vote button the desired behavior is for the FeedItem.vote() method to send an object up to the main Feed component:

var FeedList = React.createClass({

  render: function() {
    var feedItems = this.props.items;

    return (
      <div className="container">
        <ul className="list-group">

          {feedItems.map(function(item) {
            return <FeedItem key={item.key}
                             title={item.title}
                             desc={item.description}
                             voteCount={item.voteCount}
                             onVote={this.props.onVote} />
          }.bind(this))}
        </ul>
      </div>
    );
  }

});

Which should pass that key on throught the parent component's onVote function:

var Feed = React.createClass({

  getInitialState: function () {
    var FEED_ITEMS = [
      {
        key: 1,
        title: 'JavaScript is fun',
        description: 'Lexical scoping FTW',
        voteCount: 34
      }, {
        key: 2,
        title: 'Realtime data!',
        description: 'Firebase is cool',
        voteCount: 49
      }, {
        key: 3,
        title: 'Coffee makes you awake',
        description: 'Drink responsibly',
        voteCount: 15
      }
    ];
    return {
      items: FEED_ITEMS,
      formDisplayed: false
    }
  },

  onToggleForm: function () {
    this.setState({
      formDisplayed: !this.state.formDisplayed
    });
  },

  onNewItem: function (newItem) {
    var newItems = this.state.items.concat([newItem]);
    // console.log("Creating these items: ", newItems);
    this.setState({
      items: newItems,
      formDisplayed: false,
      key: this.state.items.length
    });
  },

  onVote: function (newItem) {
    // console.log(item);

    var items = _.uniq(this.state.items);
    var index = _.findIndex(items, function (feedItems) {
      // Not getting the correct index.
      console.log("Does ", feedItems.key, " === ", newItem.key, "?");
      return feedItems.key === newItem.key;
    });
    var oldObj = items[index];
    var newItems = _.pull(items, oldObj);
    var newItems = this.state.items.concat([newItem]);
    // newItems.push(item);
    this.setState({
      items: newItems
    });
  },

  render: function () {
    return (
      <div>
        <div className="container">
          <ShowAddButton displayed={this.state.formDisplayed} onToggleForm={this.onToggleForm}/>
        </div>
        <FeedForm displayed={this.state.formDisplayed} onNewItem={this.onNewItem}/>
        <br />
        <br />
        <FeedList items={this.state.items} onVote={this.onVote}/>
      </div>
    );
  }

});

My logic relies on being able to reconcile the keys in the onVote function, however the key prop is not being properly passed on. So my question is, how do I pass on they key through this 'one way flow' to my parent component?

Note: Feel free to point out other problems or better design decision, or absolute stupidities. Or even that I'm asking the wrong question.

Looking forward to a nice exploration of this cool framework.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The key prop has a special meaning in React. It it is not passed to the component as prop but is used by React to aid the reconciliation of collections. If you know d3, it works similar like the key function for selection.data(). It allows React to associate the elements of the previous tree with the elements of the next tree.

It's good that you have a key (and you need one if you pass an array of elements), but if you want to pass that value along to the component, you should another prop:

<FeedItem key={item.key} id={item.key} ... />

(and access this.props.id inside the component).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...