Cómo evitar funciones de enlace o flecha en línea dentro del método de renderizado

Resuelto Mayank Shukla asked hace 7 años • 4 respuestas

Deberíamos evitar la vinculación de métodos dentro del renderizado porque durante el nuevo renderizado se crearán nuevos métodos en lugar de usar el antiguo, lo que afectará el rendimiento.

Entonces, para escenarios como este:

<input onChange = { this._handleChange.bind(this) } ...../>

Podemos vincular _handleChangeel método ya sea en el constructor:

this._handleChange = this._handleChange.bind(this);

O podemos usar la sintaxis del inicializador de propiedad :

_handleChange = () => {....}

Ahora consideremos el caso en el que queremos pasar algún parámetro adicional, digamos en una aplicación de tareas simple, al hacer clic en el elemento necesito eliminar el elemento de la matriz, para eso necesito pasar el índice del elemento o el nombre de la tarea en cada uno. Método al hacer clic:

todos.map(el => <div key={el} onClick={this._deleteTodo.bind(this, el)}> {el} </div>)

Por ahora simplemente asuma que los nombres de las tareas pendientes son únicos.

Según DOC :

El problema con esta sintaxis es que se crea una devolución de llamada diferente cada vez que se procesa el componente.

Pregunta:

¿Cómo evitar esta forma de vincularse dentro del método de renderizado o cuáles son las alternativas a esto?

Por favor proporcione cualquier referencia o ejemplo, gracias.

Mayank Shukla avatar Jul 12 '17 16:07 Mayank Shukla
Aceptado

Primero: una solución simple será crear un componente para el contenido dentro de una función de mapa y pasar los valores como accesorios y cuando llame a la función desde el componente secundario, podrá pasar el valor a la función transmitida como accesorios.

Padre

deleteTodo = (val) => {
    console.log(val)
}
todos.map(el => 
    <MyComponent val={el} onClick={this.deleteTodo}/> 

)

Mi componente

class MyComponent extends React.Component {
    deleteTodo = () => {
        this.props.onClick(this.props.val);
    }
    render() {
       return <div  onClick={this.deleteTodo}> {this.props.val} </div>
    }
}

Fragmento de muestra

class Parent extends React.Component {
     _deleteTodo = (val) => {
        console.log(val)
    }
    render() {
        var todos = ['a', 'b', 'c'];
        return (
           <div>{todos.map(el => 
             <MyComponent key={el} val={el} onClick={this._deleteTodo}/> 
        
           )}</div>
        )
    }
    
   
}

class MyComponent extends React.Component {
        _deleteTodo = () => {
                     console.log('here');   this.props.onClick(this.props.val);
        }
        render() {
           return <div onClick={this._deleteTodo}> {this.props.val} </div>
        }
    }
    
ReactDOM.render(<Parent/>, document.getElementById('app'));
<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="app"></div>
Expandir fragmento

EDITAR:

Segundo: el otro enfoque sería usar memorizar y devolver una función.

constructor() {
    super();
    this._deleteTodoListener = _.memoize(
                   this._deleteTodo, (element) => {
                        return element.hashCode();
                    }
              )
}

_deleteTodo = (element) => {
   //delete handling here
}

y usarlo como

todos.map(el => <div key={el} onClick={this._deleteTodoListener(el)}> {el} </div>)

PD: Sin embargo, esta no es la mejor solución y aún así resultará en la creación de múltiples funciones, pero sigue siendo una mejora con respecto al caso inicial.

Tercero: sin embargo, una solución más apropiada para esto será agregar un attributeal div superior y obtener el valor de eventlike

_deleteTodo = (e) => {
     console.log(e.currentTarget.getAttribute('data-value'));

 }

 todos.map(el => <div key={el} data-value={el} onClick={this._deleteTodo}> {el} </div>)

Sin embargo, en este caso los atributos se convierten a cadena usando el método toString y, por lo tanto, el objeto se convertirá en [Object Object]una matriz ["1" , "2", "3"]como"1, 2, 3"

Shubham Khatri avatar Jul 12 '2017 09:07 Shubham Khatri

¿Cómo evitar esta forma de vincularse dentro del método de renderizado o cuáles son las alternativas a esto?

Si te interesa volver a renderizar, entonces shouldComponentUpdateeres PureComponenttus amigos y ellos te ayudarán a optimizar el renderizado.

Tienes que extraer el componente "Secundario" del "Padre" y pasar siempre los mismos accesorios e implementar shouldComponentUpdateo usar PureComponent. Lo que queremos es un caso en el que eliminemos un niño, otros niños no deberían volver a representarse.

Ejemplo

import React, { Component, PureComponent } from 'react';
import { render } from 'react-dom';

class Product extends PureComponent {
  render() {
    const { id, name, onDelete } = this.props;

    console.log(`<Product id=${id} /> render()`);
    return (
      <li>
        {id} - {name}
        <button onClick={() => onDelete(id)}>Delete</button>
      </li>
    );
  }
}

class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      products: [
        { id: 1, name: 'Foo' },
        { id: 2, name: 'Bar' },
      ],
    };

    this.handleDelete = this.handleDelete.bind(this);
  }

  handleDelete(productId) {
    this.setState(prevState => ({
      products: prevState.products.filter(product => product.id !== productId),
    }));
  }

  render() {
    console.log(`<App /> render()`);
    return (
      <div>
        <h1>Products</h1>
        <ul>
          {
            this.state.products.map(product => (
              <Product 
                key={product.id}
                onDelete={this.handleDelete}
                {...product}
              />
            ))
          }
        </ul>
      </div>
    ); 
  }
}

render(<App />, document.getElementById('root'));

Demostración: https://codesandbox.io/s/99nZGlyZ

Comportamiento esperado

  • <App /> render()
  • <Product id=1... render()
  • <Product id=2... render()

Cuando eliminamos <Product id=2 ...solo <App />se vuelve a renderizar.

  • prestar()

Para ver esos mensajes en la demostración, abra la consola de herramientas de desarrollo.

La misma técnica se utiliza y se describe en el artículo: React es lento, React es rápido: optimización de las aplicaciones React en la práctica de François Zaninotto.

Dawid Karabin avatar Jul 12 '2017 09:07 Dawid Karabin

La documentación recomienda utilizar atributos de datos y acceder a ellos desde dentro evt.target.dataset:

_deleteTodo = (evt) => {
  const elementToDelete = evt.target.dataset.el;
  this.setState(prevState => ({
    todos: prevState.todos.filter(el => el !== elementToDelete)
  }))
}

// and from render:

todos.map(
  el => <div key={el} data-el={el} onClick={this._deleteTodo}> {el} </div>
)

También tenga en cuenta que esto tiene sentido sólo cuando tiene problemas de rendimiento:

¿Está bien utilizar funciones de flecha en los métodos de renderizado?

En términos generales, sí, está bien y, a menudo, es la forma más sencilla de pasar parámetros a funciones de devolución de llamada.

Si tiene problemas de rendimiento, ¡optimice!

n1stre avatar Nov 13 '2018 09:11 n1stre