Joseph
Joseph

Reputation: 13178

React: Unable to access child props in parent's event handler

I'm using React to create a UI and I have a parent component and a child component, something along these lines:

// Child component
var ListItem = React.createClass({
  render: function() {
    var link_details = (
      <div>
          Start Date: {this.props.my_data.start_date}<br/>
          End Date: {this.props.my_data.end_date}<br/>
      </div>
    );

    return (
      <li>
          <a onClick={this.props.clickHandler}>
            { this.props.my_data.name }
          </a>
          {link_details}
      </li>
    )
  }
});

// Parent component
var Sidebar = React.createClass({
  getInitialState: function() {
    return {
        my_data: [],
    };
  },
  handleListItemClick: function(e){
    console.log(e.target);
    console.log(e.target.props);  
  },
  render: function() {
    var myLinks = this.state.my_data.map(function(mylink) {
        return (
          <ListItem key={mylink.id} my_data={mylink} clickHandler={this.handleListItemClick} />
        );
    }.bind(this));
    return (
    <div>
      <ul className="nav nav-sidebar">
        { myLinks }
      </ul>
    </div>)
  }
});

I want the click event on the child to trigger the parent's handler so that the parent can update its state based on what was clicked in the child. While the code I have above works, and the parent's handler is called, I am unable to access any of the child component's props. I'm not sure if that's by design and I should pass data from the child to the parent in a different way, or if I'm doing something wrong. I'm still very new to React, so any advice is appreciated. Thanks!

Upvotes: 1

Views: 1534

Answers (3)

WitVault
WitVault

Reputation: 24130

You can not do that but you can pass data from child to parent via callback

<li>
  <a onClick={this.props.clickHandler.bind(null,this.props.my_data.name)}>
    { this.props.my_data.name }
  </a>
  {link_details}
</li>

or using arrow function if you are using es6

<li>
  <a onClick={() => this.props.clickHandler(this.props.my_data.name)}>
    { this.props.my_data.name }
  </a>
  {link_details}
</li>

Edit

Why passing null?

Things to remember: Automatic binding methods to 'this' happens when your component mounts.

There are two conditions

1.Calling a callback passed from parent component to a child component

When we directly pass functions (e.g. this.clickHandler) to a child component without worrying about the value of 'this' when the function is actually called.

React then the replaces the standard Function.prototype.bind method with its own function to help stop you from doing anything silly (like trying to change the already-bound value of 'this'), so you instead have to pass 'null' to say "I understand this will only alter the arguments".

2.Calling a function defined within same component

React does not do this for function calls within the same component

Rules for binding

If you want to set the first argument by calling .bind on a function...

passed in via props, pass null as the first argument e.g.

this.props.funcName.bind(null, "args")

taken from 'this', pass 'this' as the first argument e.g.

this.funcName.bind(this, "args")

Upvotes: 4

Joseph
Joseph

Reputation: 13178

I took a look at the answer on Pass props to parent component in React.js and came up with the following:

// Parent component
var Sidebar = React.createClass({
  getInitialState: function() {
    return {
        my_data: [],
    };
  },
  handleListItemClick: function(data_passed, e){
    console.log(data_passed);
  },
  render: function() {
    var myLinks = this.state.my_data.map(function(mylink) {
        return (
          <ListItem key={mylink.id} my_data={mylink} clickHandler={this.handleListItemClick.bind(null, mylink.id)} />
        );
    }.bind(this));
    return (
    <div>
      <ul className="nav nav-sidebar">
        { myLinks }
      </ul>
    </div>)
  }
});

This does seem to work- I'd be interested in seeing other solutions and which one is the "best" and why.

Upvotes: 0

Dmitriy
Dmitriy

Reputation: 3765

You can do so:

var ListItem = React.createClass({
  clickItem: function (e) {
      this.props.clickHandler(e, this.props.my_data); // now you can pass any data to parent
  },
  render: function() {
    var link_details = (
      <div>
          Start Date: {this.props.my_data.start_date}<br/>
          End Date: {this.props.my_data.end_date}<br/>
      </div>
    );

    return (
      <li>
          <a onClick={this.clickItem}>
            { this.props.my_data.name }
          </a>
          {link_details}
      </li>
    )
  }
});

Upvotes: 1

Related Questions