¿ Cuál es el objetivo de usar bind(this) en un método en JS?

publicado por: Anonymous

¿Por qué en el método filter tengo que usar .bind(this) como sigue: onChange={this.filter.bind(this)}, en vez de solo usar el método filter sin .bind(this) como en onChange={this.filter}?

export default class TodoList extends React.Component {
  filter(event) {
    this.props.store.filter = event.target.value
  }

  render() {
    return <div>
      <h1>toDos</h1>
      <input className="filter" onChange={this.filter.bind(this)} />

    </div>
  }
}

solución

Eso es una restricción de javascript, debido a que el parámetro que pasas al event handler onChange puede venir desde cualquier fuente y no necesariamente desde el componente actual. Pon el siguiente ejemplo en que la función filter está definida en un componente que usa a TodoList:

export default class TodoApp extends React.Component {
  filter(event) {
    this.props.store.filter = event.target.value
  }

  render() {
    return 
      <div>
        <TodoList filter={this.filter} />
      </div>
  }
}


export default class TodoList extends React.Component {
  render() {
    let { filter } = this.props
    return 
      <div>
        <h1>toDos</h1>
        <input className="filter" onChange={filter} />
      </div>
  }
}

¿Cual debería ser para este caso, dentro del contexto de la función filter, el valor de this? ¿El componente TodoApp o TodoList?
Por tanto ES6 te fuerza a especificar manualmente cual será el contexto para this. Para el ejemplo descrito, si quisieras que this fuese TodoApp usarías:

        <TodoList filter={this.filter.bind(this)} />

Y si quisieras que fuese TodoList, usarías:

        <input className="filter" onChange={filter.bind(this)} />

Una forma más limpia de realizar esto es definir los bind dentro del constructor del componente, en vez de hacerlo inline:

export default class TodoList extends React.Component {
  constructor(props) {
    super(props);
    this.filter = this.filter.bind(this);
  }

  filter(event) {
    this.props.store.filter = event.target.value
  }

  render() {
    return <div>
      <h1>toDos</h1>
      <input className="filter" onChange={this.filter} />

    </div>
  }
}

Otra forma, que puedes encontrarte en varios ejemplos de react en internet, es usando React.createClass, la cual internamente trae algo de azúcar sintáctica para evitarte hacer los bind por tu cuenta:

const TodoList = React.createClass({
  filter(event) {
    this.props.store.filter = event.target.value
  },

  render() {
    return 
      <div>
        <h1>toDos</h1>
        <input className="filter" onChange={this.filter} />
      </div>
  }
});

export default TodoList;

Ojo que aquí se está usando ES5, a diferencia de cuando extiendes tu clase desde React.Component que es ES6. A pesar de que en el fondo hacen lo mismo, existen otras diferencias sutiles entre ambas, las que puedes consultar en éste artículo.

Una última opción sería usar un arrow => de ES6, el cual hace un bind(this) internamente:

export default class TodoList extends React.Component {
  filter = (event) => {
    this.props.store.filter = event.target.value
  }

  render() {
    return <div>
      <h1>toDos</h1>
      <input className="filter" onChange={this.filter} />

    </div>
  }
}

sin embargo, si pretendes pasar parámetros a tu función (por ejemplo onChange={this.filter('name')} , deberás tener una precaución extra con el operador =>. Tu función debería ser como sigue:

export default class TodoList extends React.Component {
  filter = (element) => (event) => {
    event.preventDefault();
    this.props.store.filter = element;
  }

  render() {
    return <div>
      <h1>toDos</h1>
      <input className="filter" onChange={this.filter('name')} />

    </div>
  }
}
Respondido por: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *