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?

1
  • I do not know React, but my guess would be that if the arguments are already bound then the handler should remain unchanged: <div onClick={this._onClick}> Commented Feb 14, 2017 at 16:50

4 Answers 4

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);
  }
}
Sign up to request clarification or add additional context in comments.

Comments

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.

2 Comments

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 (github.com/yannickcr/eslint-plugin-react/issues/1444) this solution not even prevents the warning.
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
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.

Comments

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>
    );
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.