Saltar al contenido

React.js: la forma más eficiente de pasar un parámetro a un controlador de eventos sin bind () en un componente

Solución:

Lo he explicado en mi otra publicación: evento de clic en el componente de reacción.

Nunca use la función de flecha en línea si le preocupa su rendimiento. Todavía puede usar el método de clase pública y vincular el contexto this.

handleClick = () => () => {
    this.setState({ // this works now
      isClicked:true
    });
}

Puede pasar cualquier parámetro que desee así:

handleClick = (param1, param2, param3) => (event) => {

Según el comentario de devserkan,

Esto es curry y lo mismo que otras opciones. Esta función también se recrea en cada render.

No. No es así. Vea la nota de los documentos:

Si esta devolución de llamada se pasa como un apoyo a los componentes inferiores, esos componentes podrían hacer una re-renderización adicional. Generalmente recomendamos enlazar en el constructor o usar el sintaxis de campos de clase, para evitar este tipo de problemas de rendimiento.

Además, vea el comentario de bigga-hd debajo de la respuesta de cierto rendimiento:

Evite declarar funciones de flecha o enlazar en render para un rendimiento óptimo. Declare sus funciones fuera de render. No más asignaciones de funciones en cada render.


¿Cómo se llama a este controlador?

Puede llamar al método así:

onClick={this.handleClick(param1,param2,param3)}

PD: No marqué esta publicación como duplicada ya que el alcance de la pregunta es significativamente diferente. Por lo tanto, solo vincule la publicación para que profundice en más detalles.

En lugar de .binding o creando una función de flecha anónima en render(), puede crear la función vinculada / anónima fuera de de render(), como en el objeto instanciado, en el constructor, o algo así, y use una referencia a ese singular (nunca recreado) función. Por ejemplo, ejecuta una vez:

this.boundHandleClick = this.handleClick.bind(this, 111);

o

this.boundHandleClick = () => this.handleClick(111);

Entonces, en render, referencia boundHandleClick:

return (
  <div className="App">
    <button onClick={this.boundHandleClick}>Click</button>
  </div>
);

Si necesita utilizar los parámetros (111) dentro de render, entonces puede usar la búsqueda de objetos para ver si existe una función enlazada con ese parámetro. Si lo hace, solo use esa función vinculada; de lo contrario, créela (una vez, por lo que no tendrá que crearse nuevamente cada vez que use ese parámetro en el futuro):

this.boundClicks = {};
// ...
if (!this.boundClicks['111']) this.boundClicks['111'] = () => this.handleClick(111);
return (
  <div className="App">
    <button onClick={this.boundClicks['111']}>Click</button>
  </div>
);

Esto depende de cómo obtenga el parámetro. Habrá ocasiones en las que no podrá evitar el uso de .bind o una función de flecha fácilmente, pero la mayoría de las veces puede obtener el parámetro de alguna manera. Como puede ver en la respuesta de @ CertainPerformance, si puede usar este argumento en el constructor, puede preferir de esta manera. Pero puede haber otros enfoques.

Por ejemplo, suponga que tiene una lista en el estado. En lugar de mapear esta lista directamente y usar un .bind o una función de flecha allí, puede pasar los elementos de la lista a un componente secundario y luego usar un controlador de devolución de llamada allí.

class App extends React.Component {
  state = {
    list: [ "foo", "bar" ],
  };

  handleClick(el) { console.log( el ) }

  render() {
    return (
      <div>
        {this.state.list.map( el => (
          <Child key={el} el={el} onClick={this.handleClick} />
        ) )}
      </div>
    );
  }
}

const Child = ( props ) => {
  const handleClick = () => props.onClick( props.el );
  return (
    <div>
      {props.el}
      <button onClick={handleClick}>Click</button>
    </div>
  );
};

ReactDOM.render( <App />, document.getElementById( "root" ) );
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Estoy actualizando mi respuesta con una demostración de cómo usar una función de flecha en línea, vincularla o usar una función curry provoca una recreación aquí.

Suponga que tiene un componente y este componente tiene un componente hijo escrito como React.PureComponent. Normalmente, si los accesorios de este niño no cambian, no se volverá a renderizar. Frio. Tenemos un método de clase en nuestro componente padre y queremos pasarlo como un controlador a nuestro componente hijo. Veamos qué está pasando aquí.

Primero, no paso el controlador y cuando incrementa el contador en el padre, el componente secundario no se vuelve a procesar (excepto el procesamiento inicial). Esto se debe a que lo definimos como PureComponent. No queremos que se vuelva a reproducir a menos que cambie su utilería.

class App extends React.Component {
  state = {
    counter: 0,
  };

  increment = () =>
    this.setState( currentState => ( {
      counter: currentState.counter + 1,
    } ) );

  handleClick(param) { console.log( param ) }

  render() {
    return (
      <div>
        <button onClick={this.increment}>Increment</button>
        Counter is: {this.state.counter}
        <Child />
      </div>
    );
  }
}

class Child extends React.PureComponent {
  render() {
    console.log( "child rendered" );
    return (
      <div>
        <button onClick={this.props.onClick}>Click</button>
      </div>
    );
  }
}

ReactDOM.render( <App />, document.getElementById( "root" ) );
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<div id="root"></div>

Como puede ver, el componente hijo no se vuelve a generar. Ahora hagamos esto con nuestro método de clase, usando una función de flecha en línea.

class App extends React.Component {
  state = {
    counter: 0,
  };

  increment = () =>
    this.setState( currentState => ( {
      counter: currentState.counter + 1,
    } ) );

  handleClick( param ) { console.log( param ) }

  render() {
    return (
      <div>
        <button onClick={this.increment}>Increment</button>
        Counter is: {this.state.counter}
        <Child onClick={() => this.handleClick( "some param" )} />
      </div>
    );
  }
}

class Child extends React.PureComponent {
  render() {
    console.log( "child rendered" );
    return (
      <div>
        <button onClick={this.props.onClick}>Click</button>
      </div>
    );
  }
}

ReactDOM.render( <App />, document.getElementById( "root" ) );
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<div id="root"></div>

Vaya, el niño se representa cuando incrementamos el contador. Pero, no tiene ninguna relación con el estado contrario, no queremos esto. Entonces, ¿por qué se está rindiendo? Esto se debe a que estamos usando una función de flecha en línea en el onClick apoyo que está consiguiendo. Dado que esta función se recrea en cada renderizado del padre, su referencia cambia a una función diferente y el niño piensa que obtiene un nuevo accesorio. Pero en realidad no lo consigue. Podemos usar el parámetro con nuestro controlador, pero hay una representación innecesaria.

Ahora con el .bind. Yo no uso this en el aprieto ya que no usamos this en nuestro método simple. Simplemente registra un parámetro.

class App extends React.Component {
  state = {
    counter: 0,
  };

  increment = () =>
    this.setState( currentState => ( {
      counter: currentState.counter + 1,
    } ) );

  handleClick( param ) { console.log( param ) }

  render() {
    return (
      <div>
        <button onClick={this.increment}>Increment</button>
        Counter is: {this.state.counter}
        <Child onClick={this.handleClick.bind( null, "some param" )} />
      </div>
    );
  }
}

class Child extends React.PureComponent {
  render() {
    console.log( "child rendered" );
    return (
      <div>
        <button onClick={this.props.onClick}>Click</button>
      </div>
    );
  }
}

ReactDOM.render( <App />, document.getElementById( "root" ) );
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<div id="root"></div>

Lo mismo aquí, podemos usar el parámetro pero hay renderizado innecesario. Ahora con función al curry.

class App extends React.Component {
  state = {
    counter: 0,
  };

  increment = () =>
    this.setState( currentState => ( {
      counter: currentState.counter + 1,
    } ) );

  handleClick( param ) { 
    return function() {
      console.log( param ) 
    }
  }

  render() {
    return (
      <div>
        <button onClick={this.increment}>Increment</button>
        Counter is: {this.state.counter}
        <Child onClick={this.handleClick( "some param" )} />
      </div>
    );
  }
}

class Child extends React.PureComponent {
  render() {
    console.log( "child rendered" );
    return (
      <div>
        <button onClick={this.props.onClick}>Click</button>
      </div>
    );
  }
}

ReactDOM.render( <App />, document.getElementById( "root" ) );
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<div id="root"></div>

¡Sorpresa! Nuevamente renderizado innecesario. Ahora bien, para un componente esto no es tan importante. Pero, ¿qué pasa si su aplicación tiene cientos de componentes como este niño?

Ahora, supongamos que estoy obteniendo el parámetro de alguna manera. Lo estoy imitando con una cadena codificada aquí.

class App extends React.Component {
  state = {
    counter: 0,
  };

  increment = () =>
    this.setState( currentState => ( {
      counter: currentState.counter + 1,
    } ) );

  handleClick() { console.log( "some param" ) }

  render() {
    return (
      <div>
        <button onClick={this.increment}>Increment</button>
        Counter is: {this.state.counter}
        <Child onClick={this.handleClick} />
      </div>
    );
  }
}

class Child extends React.PureComponent {
  render() {
    console.log( "child rendered" );
    return (
      <div>
        <button onClick={this.props.onClick}>Click</button>
      </div>
    );
  }
}

ReactDOM.render( <App />, document.getElementById( "root" ) );
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<div id="root"></div>

¡Duh! No es necesario volver a renderizar como se esperaba, ya que usamos la función de referencia. Puedo usar el parámetro, pero la vida no es tan fácil y OP pregunta cómo podemos usar el parámetro sin usar una función de flecha en línea, o vinculante o con una función curry. Todo el alboroto se trata de esto.

Aunque no pasamos este controlador a un componente, todavía se recrea en cada render del padre como vemos aquí. Si tiene una lista de elementos, digamos 500 de ellos, y los está mapeando en los botones en el componente principal y usa una función de flecha, etc. aquí, ¡esto significa que se volverán a crear (500 veces) en cada renderizado!

Entonces, no hay una manera fácil de hacer esto. Si nuestro parámetro no proviene del objeto de evento, usamos la solución de @ CertainPerformance o intentamos cambiar nuestra lógica como lo hago aquí.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *