9

I'm trying to follow the no-bind rule for React using the pattern that they have recommended with ES6 classes:

class Foo extends React.Component {
  constructor() {
    super();
    this._onClick = this._onClick.bind(this);
  }
  render() {
    return (
      <div onClick={this._onClick}>
        Hello!
      </div>
    );
  }
  _onClick() {
    // Do whatever you like, referencing "this" as appropriate
  }
}

However, when I need to pass arguments in to _onClick, what needs to change?

I've tried something like:

import {someFunc} from 'some/path';

class Foo extends React.Component {
  constructor() {
    super();
    this._onClick = this._onClick.bind(this, a, b);
  }
  render() {
    const {
     prop1,
     prop2
    } = this.props;

    return (
      <div onClick={this._onClick(prop1, prop2}>
        Hello!
      </div>
    );
  }
  _onClick = (a, b) => {
    someFunc(a, b);
  }
}

However, this does not work. What needs to be altered?

TheRealFakeNews
  • 7,512
  • 16
  • 73
  • 114

4 Answers4

8

The call to bind in the constructor should only pass this as a single argument.

this._onClick = this._onClick.bind(this);

Here you are overwriting the property this._onClick with a new one that has the correct this bound. If your function takes two arguments, then you should pass those as normal at call time.

Passing additional arguments to bind means that the function returned already has those arguments supplied - in your attempt the _onClick function will always have its first two arguments undefined, as a and b have no value in the constructor.

Now that you have bound this to your function, you can access this.props from within there, rather than having to pass arguments:

import {someFunc} from 'some/path';

class Foo extends React.Component {
  constructor() {
    super();
    this._onClick = this._onClick.bind(this);
  }
  render() {
    return (
      <div onClick={this._onClick}>
        Hello!
      </div>
    );
  }
  _onClick() {
    const {
     prop1,
     prop2
    } = this.props;
    someFunc(prop1, prop2);
  }
}
Tom Fenech
  • 72,334
  • 12
  • 107
  • 141
5

You should use partial application. Basically you initialise your onClick function with the parameters you want, and the onClick function will return a new function to be called when the div is clicked.

import {someFunc} from 'some/path';

class Foo extends React.Component {
  render() {
    return (
      <div onClick={this._onClick(prop1, prop2)}>
        Hello!
      </div>
    );
  }
  _onClick = (a, b) => {
    return () => someFunc(a, b);
  }
}

PS: this only applies if your parameters a and b are not part of your this.props, if they are then you should just do as Tom Fenech said.

Manish
  • 4,903
  • 1
  • 29
  • 39
Canastro
  • 2,979
  • 30
  • 39
  • This solution is totally wrong. It just prevents the ESLint warning. But the problem of creating a new function on every render is still there delegated to an intermediary function. EDIT: since this merge (https://github.com/yannickcr/eslint-plugin-react/issues/1444) this solution not even prevents the warning. – AxeEffect Nov 06 '17 at 14:59
  • This is a very bad solution in terms of optimization! _onClick here returns a new function each time. Even if two functions have the same function definition, they will fail the equality test since two separate objects are never equal – alaboudi May 13 '18 at 02:44
1

To answer your question there is nothing special you have to do in order to pass arguments to your this._onClick function.

the proper revised code will be:

class Foo extends React.Component {
  constructor() {
    super();
    this._onClick = this._onClick.bind(this);
  }
  render() {
    return (
      <div onClick={() => this._onClick(1, 2)}>
        Hello!
      </div>
    );
  }
  _onClick = (a, b) => {
    console.log(a, b);
  }
}

Secondly, the way you are calling this._onClick is not the right way to invoke a function on click.

Right now what is happening that on each render process your function is getting called because you didn't pass the function as an argument rather you invoked that function and assigned its returned value to the onClick prop.

you have to do this like:

render() {
  return (
    <div onClick={() => this._onClick(prop1, prop2)}>
      Hello!
    </div>
  );
}

By Invoking your function this way you ensure the this._onClick will get called when click event occurs.

1

Another method is to use Babel stage 1 autobind and skip the constructor.

import {someFunc} from 'some/path';

class Foo extends React.Component {
  _onClick = () => {
    const {
     prop1,
     prop2
    } = this.props;
    someFunc(prop1, prop2);
  }

  render() {
    return (
      <div onClick={this._onClick}>
        Hello!
      </div>
    );
  }
}
J. Mark Stevens
  • 4,911
  • 2
  • 13
  • 18