Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

react.js call parent function from child

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')
);
like image 711
Joelio Avatar asked Jan 18 '16 16:01

Joelio


Video Answer


2 Answers

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 */ );
},
like image 199
Oleksandr T. Avatar answered Nov 15 '22 15:11

Oleksandr T.


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} />
like image 21
Joseph Avatar answered Nov 15 '22 14:11

Joseph