ohadinho ohadinho - 2 months ago 9
React JSX Question

ReactJS - MouseClick gets triggered without a click

I'm new to React.JS and trying to create a click event on an element inside a rendered component.

Here is my code:

class InputPanel extends React.Component{

handleClick(i,j) {
this.props.dispatch(actions.someMethod());
// e.preventDefault();
}

render() {
const { dispatch, board } = this.props;

return(
<div>
{
board.map((row, i) => (
<div>{row.map((cell, j) => <div className="digit"
onClick={this.handleClick(i,j)}>{cell}</div>)}</div>
))
}
</div>
);

}
};


My problem is that "handleClick" gets triggered after page load without any mouse clicked!

I've read about React.JS lifecycle and thought about registering to click event in
componentDidMount
method, but i'm really not sure about it:


  1. Is there any easier way ? (or: Am I doing something wrong that triggers click ?)

  2. If adding
    componentDidMount
    method is the right way - how can I get the element I create in
    render
    method ?


Answer

You should not use .bind when passing the callback as a prop. There’s a ESLint rule for that. You can read more about how to pass callback without breaking React performance here.

Summary:

  1. make sure you aren’t calling functions but pass functions as handlers in your props.
  2. make sure you do not create functions on every render, for that, you need to bind your handlers in parent component, pass correct the required data (such as indices of iteration) down the child component and have it call the parent’s handler with the data it has

Ideally you’d create another component for the rows and pass the callback there. Moreover, ideally you’d bind the onClick in the parent component’s constructor (or componentWillMount). Otherwise every time render runs a new function is created (in both anonymous function handler () => { this.onClick() } and this.onClick.bind and defeat React’s vdom diff causing every row to rerender every time.

So:

class InputPanel extends React.Component{
  constructor() {
    super();
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick(i,j) {    
    this.props.dispatch(actions.someMethod());
   // e.preventDefault();
  }  

  render() {
    const { dispatch, board } = this.props;

    return(
   <div>           
      {board.map((row, i) => <div>
          {row.map((cell, j) => <Digit 
            onClick={this.handleClick})
            i={i} 
            j={j}
            cell={cell}
          />)}
      </div>)}
  </div>
   );

 }
};

class Digit extends React.Component {
  constructor() {
    super();
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    this.props.onClick(this.props.i, this.props.j);
  }

  render() {
    return <div 
      className="digit"
      onClick={this.handleClick}
    >{this.props.cell}</div>
  }
}