1

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!

Joseph
  • 12,678
  • 19
  • 76
  • 115

3 Answers3

4

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")
WitVault
  • 23,445
  • 19
  • 103
  • 133
  • I tried this and while it seems to work, I get a warning in my console: `Warning: bind(): React component methods may only be bound to the component instance.` Changing the `.bind(this, this.props.my_data_name`) to `.bind(null, this.props.my_data_name)` eliminates the error, but I'm not exactly sure what the consequences of this change are. – Joseph Mar 18 '16 at 21:18
  • `{this.props.clickHandler.bind(null,this.props.my_data_name)}` is the correct way to do it. – WitVault Mar 18 '16 at 21:23
  • Edited the answer. – WitVault Mar 18 '16 at 21:28
  • Presumably there's a typo in either `my_data_name` or `my_data.name`. They should both be the same. – jdunning Oct 07 '17 at 20:11
1

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>
    )
  }
});
Dmitriy
  • 3,745
  • 16
  • 24
  • Yup. Also `onClick={() => this.clickHandler(this.props)}` – azium Mar 18 '16 at 21:13
  • Interesting way to do it, thanks! I'd be curious to see this compared to the answer I posted below. I think this one is a bit more lengthy (requires an event handler on the child and the parent) and I tend to favor writing less code, so does this answer offer anything that the other does not? – Joseph Mar 18 '16 at 21:15
  • Yes. Unlike @WitVault's answer, this solution doesn't create a new handler function on every render pass. The `clickItem()` function is instantiated once and then shared among all the `ListItem` instances. It should be more performant and require less memory. – jdunning Oct 07 '17 at 20:15
0

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.

Community
  • 1
  • 1
Joseph
  • 12,678
  • 19
  • 76
  • 115
  • Yea not this.. do what @Dmitriy said or what I said in my comment on his answer. – azium Mar 18 '16 at 21:15
  • Can you explain why "not this"? What are the differences? Why is this way bad? – Joseph Mar 18 '16 at 21:16
  • 1
    1) this solution works because you know the value you want ahead of time in your parent (the item id). but a lot of the times you don't.. so just pass the props from your child as an argument. much more common. 2) more importantly, calling `bind` inside a `render` method will add a million new function instances in memory. best to just use a normal function – azium Mar 18 '16 at 21:18