9

I know there are a few similar questions here and here but I am having a tough time understanding what is the correct thinking today on this and extrapolating it to my situation.

I have a simple app, ScoreBox has a ScoreList which has many Scores. I want to have a Score onClick call ScoreList handleScoreRemove. I am showing the full js file, but the most important lines are line 5 and line 77.

var Score = React.createClass({
  removeRecord: function(e){
      // How do I do this?
      ScoreList.handleScoreRemove(e);
  },
  render: function() {
    var team1_style = (this.props.team1_score >= this.props.team2_score) ?
        {fontWeight: 'bold'} : {};
    var team2_style = (this.props.team2_score >= this.props.team1_score) ?
            {fontWeight: 'bold'} : {};
        return (
            <tr>
              <td style={team1_style}>{this.props.team1_name}:</td><td style={team1_style}>{this.props.team1_score}</td>
              <td style={team2_style}>{this.props.team2_name}:</td><td style={team2_style}>{this.props.team2_score}</td>
              <td><a hef="#" id={this.props.id} onClick={this.removeRecord}>remove</a></td>
            </tr>
    );
  }
});

var ScoreBox = React.createClass({
  loadScoresFromServer: function() {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      cache: false,
      success: function(data) {
        this.setState({data: data});
      }.bind(this),
      error: function(xhr, status, err) {
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  },
  handleScoreSubmit: function(score) {
    var scores = this.state.data;
    // Optimistically set an id on the new score. It will be replaced by an
    // id generated by the server. In a production application you would likely
    // not use Date.now() for this and would have a more robust system in place.
    score.id = Date.now();
    var newScores = scores.concat([score]);
    this.setState({data: newScores});
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      type: 'POST',
      data: score,
      success: function(data) {
        this.setState({data: data});
      }.bind(this),
      error: function(xhr, status, err) {
        this.setState({data: scores});
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  },
  getInitialState: function() {
    return {data: []};
  },
  componentDidMount: function() {
    this.loadScoresFromServer();
    setInterval(this.loadScoresFromServer, this.props.pollInterval);
  },
  render: function() {
    return (
      <div className="scoreBox">
        <h1>Scores</h1>
        <ScoreList data={this.state.data} />
        <ScoreForm onScoreSubmit={this.handleScoreSubmit} />
      </div>
    );
  }
});

var ScoreList = React.createClass({
  handleScoreRemove: function(score) {
    var scores = this.state.data;
    var index_of_score = array.indexOf(score);
    var newScores = scores.splice(index_of_score, 1);
    this.setState({data: newScores});
    $.ajax({
      url: this.props.url + "/" + score[id],
      dataType: 'json',
      type: 'DELETE',
      success: function(data) {
        this.setState({data: data});
      }.bind(this),
      error: function(xhr, status, err) {
        this.setState({data: scores});
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  },
  render: function() {
    var scoreNodes = this.props.data.map(function(score) {
      return (
        <Score key={score.id} id={score.id} team1_name={score.team1_name} team1_score={score.team1_score} team2_name={score.team2_name} team2_score={score.team2_score} >
        </Score>
      );
    });
    return (
      <div className="scoreList">
        <table>
          <tbody>
            {scoreNodes}
          </tbody>
        </table>
      </div>
    );
  }
});

var ScoreForm = React.createClass({
  checkForCompleteForm: function(){
    if (this.state.team1_name.length > 0 && this.state.team2_name.length > 0 && this.state.team1_score.length > 0 && this.state.team2_score.length > 0)
    {
      // enable the button
      $("input[type=submit]").removeAttr('disabled');
    }

  },
  getInitialState: function() {
    return {id: '', team1_name: '', team1_score: '', team2_name: '', team2_score: ''};
  },
 handleChange : function (e) {
    // this is a generic handle change function that uses the html id to set the state instead of
    // having a bunch of if statements
    var stateObject = function() {
      var returnObj = {};
      returnObj[this.target.id] = this.target.value;
      return returnObj;
    }.bind(e)();
    // setState is async which makes this painful
    //  JCN - why when I pass checkForCompleteForm as 2nd param it doesnt work, but instead I need this
    // function bind stuff... need to understand exactly what this is doing
    this.setState( stateObject, function(){
        this.checkForCompleteForm();
    }.bind(this));
  },
  handleSubmit: function(e) {
    e.preventDefault();
    var team1_name = this.state.team1_name.trim();
    var team1_score = this.state.team1_score.trim();
    var team2_name = this.state.team2_name.trim();
    var team2_score = this.state.team2_score.trim();
    if (!team1_name || !team1_score ) {
      return;
    }
    this.props.onScoreSubmit({team1_name: team1_name, team1_score: team1_score,team2_name: team2_name, team2_score: team2_score });
    this.setState({team1_name: '', team1_score: '', team2_name: '', team2_score: ''});
  },
  render: function() {
    return (
      <form className="scoreForm" onSubmit={this.handleSubmit}>
        <input
          id='team1_name'
          type="text"
          placeholder="Team1 Name"
          value={this.state.team1_name}
          onChange={this.handleChange}
        />
        <input
          id='team1_score'
          type="number"
          placeholder="Team1 Score"
          value={this.state.team1_score}
          onChange={this.handleChange}
        />
        <input
          id='team2_name'
          type="text"
          placeholder="Team2 Name"
          value={this.state.team2_name}
          onChange={this.handleChange}
        />
        <input
          id='team2_score'
          type="number"
          placeholder="Team2 Score"
          value={this.state.team2_score}
          onChange={this.handleChange}
        />
        <input type="submit" value="Post" disabled />
      </form>
    );
  }
});

ReactDOM.render(
  <ScoreBox url="/api/scores" pollInterval={2000} />,
  document.getElementById('content')
);
1
  • All 3 answers were good, I picked the one that was easiest for me to follow. Thanks to all that helped. Commented Jan 19, 2016 at 16:51

3 Answers 3

10

You need to pass handleScoreRemove through props

var scoreNodes = this.props.data.map(function(score) {
  return <Score
    key={score.id}
    id={score.id}
    team1_name={score.team1_name}
    team1_score={score.team1_score}
    team2_name={score.team2_name}
    team2_score={score.team2_score}
    handleScoreRemove={this.handleScoreRemove.bind(this)}>
  </Score>
}, this);

and in Score component call it like this

removeRecord: function(e) {
   this.props.handleScoreRemove( /* add arguments what do you need */ );
},
Sign up to request clarification or add additional context in comments.

Comments

3

call parent function from child

You don't (like what the other posts say). You pass handleScoreRemove into the child as a prop. Inside the child, you call the function by calling the prop. In the following, handleScoreRemove is passed as the onScoreRemove prop inside the child.

<Score ...stuff... onScoreRemove={this.handleScoreRemove}></Score>

You're already doing the same thing with the ScoreBox (parent) and ScoreForm (child). You're passing a reference of handleScoreSubmit as onScoreSubmit prop in the child.

<ScoreForm onScoreSubmit={this.handleScoreSubmit} />

Comments

2

You should pass handleScoreRemove as a prop to Score:

In ScoreList:

var scoreNodes = this.props.data.map(function(score) {
  return (
    <Score key={score.id} (...) handleScoreRemove={this.handleScoreRemove}>
    </Score>
  );
});

In Score:

removeRecord: function(e){
  this.props.handleScoreRemove(this);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.