Help: Get data scope of a table row being clicked

I have been playing with react fairly successfully, however I am a little stumped with how to do this. I am pretty sure I know how to work around it, but not sure if its the best way.

I’ve got a main component which makes up the user interface, with a small dumb component which represents table rows of data.

I want to be able to click on the table row, and have an action perform in the main component. I am already passing all the right information into the dumb component including the function call, I just can’t get the scope of that row into the function… I am either missing something simple, or I am barking up the wrong tree.

This is the dumb UI component verbatim:

const TradeRow = ({trade, action}) => (
    <tr onClick={action} className={(trade.action == 1) ? 'action-buy' : 'action-sell' }>
      <td>{moment(trade.date).format('YY-MM-DD')}</td>
      <td>{trade.code}</td>
      <td>{trade.volume}</td>
      <td className="currency">{trade.price}</td>
      <td className="currency">{trade.fee}</td>
    </tr>
);

In the main component, this is how I call the dumb component. In the main render() function I have {this.listTrades()} which then builds the table content. The table and all the table data work perfectly!

listTrades() {
    return this.data._trades.map((trade) => {
      return (
        <TradeRow key={trade._id} trade={trade} action={this.doSomething} />
      );
    });
  },

  doSomething(e) {
    //TODO: Show dialogue to delete row
    console.log(e);
    console.log(this);
  }

In the doSomething() function I just have no idea where to get the data from… I can’t work out where it is, both e and this are objects but they don’t seem to have the props from the UI component.

I am guessing that I just can’t do this with a dumb UI component…
I tried something special with refs, but that failed too…

Invariant Violation: Stateless function components cannot have refs.

For the time being I will just rewrite it as a proper component.

I’m guessing this is actually onCLick={doSomething} in which case what you need is onClick={doSomething.bind(this)}

Functional stateless components don’t do autobinding on events (neither do ES6 class based components). You also could do: onClick={e => this.doSomething(e)}

With either of those, this will be your component in your handler.

1 Like

Thanks for the explanation there, I ended up just making it a normal component like this:

const TradeRow = React.createClass({
  confirmDelete() {
    this.props.action(this.props.trade);
  },

  render() {
    return (
      <tr onClick={this.confirmDelete} className={(this.props.trade.action == 1) ? 'action-buy' : 'action-sell' }>
        <td>{moment(this.props.trade.date).format('YY-MM-DD')}</td>
        <td>{this.props.trade.code}</td>
        <td>{this.props.trade.volume}</td>
        <td className="currency">{this.props.trade.price}</td>
        <td className="currency">{this.props.trade.fee}</td>
      </tr>
    )
  }
});

Ok, I missed a couple of things on my first one.

What you would want is actually pretty simple, and you still don’t need a full component.

The reason the original isn’t binding correctly is that only works on event handlers, so what we need is in your original:

<tr onClick={action.bind(trade)} />

And done.

1 Like

Thanks for the help! I am just trying to get my head around this :slight_smile:

I tried swapping back and putting in the code you suggested, it isn’t working as expected.
I now get this warning:

Warning: bind(): React component methods may only be bound to the component instance. See EditPortfolio

I’m just confused as to where this is going wrong for me.

Yeah, I should have seen that coming.

<tr onClick={() => action(trade)} />

Should work instead. The first parameter of your function will be trade (instead of this)

1 Like

oh nice, that worked!

so that is effectively ES6 shorthand for an anonymous function as the onClick function, which just calls the function passed to the dumb component with trade as the argument. so simple.

I wasn’t sure how complex you could make the bits inside the {}… thats cool.

You can put any valid javascript in there. And since JSX is run through babel, you also have all of ES6 available.