javascript - vue - typescript funcion flecha
Cómo evitar las funciones de enlace o flecha en línea dentro del método de representación (4)
¿Cómo evitar esta forma de vinculación dentro del método de renderizado o cuáles son las alternativas de esto?
Si le importa la representación, entonces
shouldComponentUpdate
y
PureComponent
son sus amigos y le ayudarán a optimizar la representación.
shouldComponentUpdate
extraer el componente "Hijo" del "Padre" y pasar siempre los mismos accesorios e implementar
shouldComponentUpdate
o usar
PureComponent
.
Lo que queremos es un caso en el que eliminamos a un niño, otros niños no deberían volver a procesarse.
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 se vuelve a procesar
<App />
.
- hacer()
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 is Slow, React is Fast: Optimizing React Apps in Practice de François Zaninotto.
Deberíamos evitar el enlace de métodos dentro del renderizado porque durante el renderizado creará los nuevos métodos en lugar de usar el antiguo, lo que afectará el rendimiento.
Entonces, para los escenarios como este:
<input onChange = { this._handleChange.bind(this) } ...../>
Podemos vincular el método
_handleChange
en el constructor:
this._handleChange = this._handleChange.bind(this);
O podemos usar la sintaxis del inicializador de propiedades :
_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 todo en cada Método onClick:
todos.map(el => <div key={el} onClick={this._deleteTodo.bind(this, el)}> {el} </div>)
Por ahora solo asuma que los nombres de tareas 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 vinculación dentro del método de renderizado o cuáles son las alternativas de esto?
Proporcione amablemente cualquier referencia o ejemplo, gracias.
Esta respuesta
https://.com/a/45053753/2808062
es definitivamente exhaustiva, pero diría que es mejor resolver este problema mediante la implementación de una
shouldComponentUpdate
adecuada de
shouldComponentUpdate
en el componente secundario.
Incluso si los accesorios son exactamente iguales, el siguiente código seguirá representando a los niños a menos que lo impidan en su propio
shouldComponentUpdate
(podrían heredarlo de
PureComponent
):
handleChildClick = itemId => {}
render() {
return this.props.array.map(itemData => <Child onClick={this.handleChildClick} data={itemData})
}
Prueba: https://jsfiddle.net/69z2wepo/92281/ .
Entonces, para evitar los renders, el componente hijo tiene que implementar
shouldComponentUpdate
todos modos.
Ahora, la única implementación razonable es ignorar completamente
onClick
independientemente de si ha cambiado:
shouldComponentUpdate(nextProps) {
return this.props.array !== nextProps.array;
}
Documentation
alienta a usar
atributos de datos
y acceder a ellos desde
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 note que esto tiene sentido solo cuando tiene problemas de rendimiento:
¿Está bien usar funciones de flecha en los métodos de renderizado?
En términos generales, sí, está bien, y a menudo es la forma más fácil de pasar parámetros a las funciones de devolución de llamada.
Si tiene problemas de rendimiento, por supuesto, ¡optimice!
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, puede 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}/>
)
MyComponent
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>
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 usándolo como
todos.map(el => <div key={el} onClick={this._deleteTodoListener(el)}> {el} </div>)
PD Sin embargo, esta no es la mejor solución y seguirá dando lugar a 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
attribute
al div superior y obtener el valor de un
event
como
_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 en cadena utilizando el método toString y, por lo tanto, el objeto se convertirá en
[Object Object]
y una matriz como
["1" , "2", "3"]
como
"1, 2, 3"