Por que no debería usar funciones de flecha en línea en accesorios JSX
Usar funciones de flecha o enlaces en JSX es una mala práctica que perjudica el rendimiento, porque la función se recrea en cada render.
Siempre que se crea una función, la función anterior se recolecta como basura. Volver a renderizar muchos elementos puede crear un jank en las animaciones.
El uso de una función de flecha en línea hará que los PureComponent
s y los componentes que se usan shallowCompare
en el shouldComponentUpdate
método se vuelvan a procesar de todos modos. Dado que el accesorio de función de flecha se recrea cada vez, la comparación superficial lo identificará como un cambio en un accesorio y el componente se volverá a procesar.
Como puede ver en los siguientes 2 ejemplos, cuando usamos la función de flecha en línea, el <Button>
componente se vuelve a generar cada vez (la consola muestra el texto del 'botón de procesamiento').
Ejemplo 1: PureComponent sin controlador en línea
class Button extends React.PureComponent {
render() {
const { onClick } = this.props;
console.log('render button');
return (
<button onClick={ onClick }>Click</button>
);
}
}
class Parent extends React.Component {
state = {
counter: 0
}
onClick = () => this.setState((prevState) => ({
counter: prevState.counter + 1
}));
render() {
const { counter } = this.state;
return (
<div>
<Button onClick={ this.onClick } />
<div>{ counter }</div>
</div>
);
}
}
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Ejemplo 2: PureComponent con controlador en línea
class Button extends React.PureComponent {
render() {
const { onClick } = this.props;
console.log('render button');
return (
<button onClick={ onClick }>Click</button>
);
}
}
class Parent extends React.Component {
state = {
counter: 0
}
render() {
const { counter } = this.state;
return (
<div>
<Button onClick={ () => this.setState((prevState) => ({
counter: prevState.counter + 1
})) } />
<div>{ counter }</div>
</div>
);
}
}
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Enlazar métodos this
sin funciones de flecha en línea
Vinculando el método manualmente en el constructor:
class Button extends React.Component {
constructor(props, context) {
super(props, context);
this.cb = this.cb.bind(this);
}
cb() {
}
render() {
return (
<button onClick={ this.cb }>Click</button>
);
}
}
Vincular un método utilizando los campos de clase de propuesta con una función de flecha. Como se trata de una propuesta de etapa 3, deberá agregar el ajuste preestablecido de etapa 3 o la transformación de propiedades de clase a su configuración de babel.
class Button extends React.Component {
cb = () => { // the class property is initialized with an arrow function that binds this to the class
}
render() {
return (
<button onClick={ this.cb }>Click</button>
);
}
}
Componentes de función con devoluciones de llamada internas
Cuando creamos una función interna (controlador de eventos, por ejemplo) dentro de un componente de función, la función se volverá a crear cada vez que se procese el componente. Si la función se pasa como accesorios (o mediante contexto) a un componente hijo ( Button
en este caso), ese hijo también se volverá a renderizar.
Ejemplo 1 - Componente de función con una devolución de llamada interna:
const { memo, useState } = React;
const Button = memo(({ onClick }) => console.log('render button') || (
<button onClick={onClick}>Click</button>
));
const Parent = () => {
const [counter, setCounter] = useState(0);
const increment = () => setCounter(counter => counter + 1); // the function is recreated all the time
return (
<div>
<Button onClick={increment} />
<div>{counter}</div>
</div>
);
}
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
Para resolver este problema, podemos envolver la devolución de llamada con el useCallback()
gancho y establecer las dependencias en una matriz vacía.
Nota: la useState
función generada acepta una función de actualización, que proporciona el estado actual. De esta forma, no es necesario establecer el estado actual como una dependencia de useCallback
.
Ejemplo 2 - Componente de función con una devolución de llamada interna envuelta con useCallback:
const { memo, useState, useCallback } = React;
const Button = memo(({ onClick }) => console.log('render button') || (
<button onClick={onClick}>Click</button>
));
const Parent = () => {
const [counter, setCounter] = useState(0);
const increment = useCallback(() => setCounter(counter => counter + 1), []);
return (
<div>
<Button onClick={increment} />
<div>{counter}</div>
</div>
);
}
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
this
, por lo que no hay nada que vincular. Por lo general, los métodos los proporciona un componente inteligente de envoltura.onClick={() => { onTodoClick(todo.id) }
cb() { onTodoClick(this.props.todo.id); }
.useCallback
con valor dinámico. stackoverflow.com/questions/55006061/…Esto se debe a que una función de flecha aparentemente creará una nueva instancia de la función en cada representación si se usa en una propiedad JSX. Esto puede crear una gran presión sobre el recolector de basura y también impedirá que el navegador optimice cualquier "ruta activa", ya que las funciones se desecharán en lugar de reutilizarse.
Puede ver la explicación completa y más información en https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md
fuente
bind
las funciones de flecha aquí es dispararse en el pie. Sin embargo, no está bien documentado, especialmente en el caso de trabajar conmap
matrices de ping dentro de Listas, etc.Para evitar crear nuevas funciones con los mismos argumentos, puede memorizar el resultado de vinculación de la función, aquí hay una utilidad simple nombrada
memobind
para hacerlo: https://github.com/supnate/memobindfuente
Usar funciones en línea como esta está perfectamente bien. La regla de pelado está desactualizada.
Esta regla es de una época en que las funciones de flecha no eran tan comunes y la gente usaba .bind (this), que solía ser lento. El problema de rendimiento se ha solucionado en Chrome 49.
Preste atención a no pasar funciones en línea como accesorios a un componente secundario.
Ryan Florence, el autor de React Router, ha escrito un gran artículo sobre esto:
https://cdb.reacttraining.com/react-inline-functions-and-performance-bdff784f5578
fuente
Puede usar las funciones de flecha usando react-cached-handler biblioteca , no hay necesidad de preocuparse por el rendimiento de renderizado:
Otras características:
fuente
¿Por qué los accesorios JSX no deberían usar funciones de flecha o vincularse?
Principalmente, porque las funciones en línea pueden romper la memorización de componentes optimizados:
Se trata menos del costo de creación de funciones adicionales:
Cuando es el
react/jsx-no-bind
útil regla?Desea asegurarse de que los componentes memorizados funcionen según lo previsto:
React.memo
(para componentes de funciones)PureComponent
o personalizadoshouldComponentUpdate
(para componentes de clase)Al obedecer esta regla, se pasan referencias a objetos de funciones estables. Por lo tanto, los componentes anteriores pueden optimizar el rendimiento al evitar que se vuelvan a renderizar, cuando los accesorios anteriores no han cambiado.
¿Cómo solucionar el error de ESLint?
Clases: defina el controlador como método o propiedad de clase para el
this
enlace.Ganchos: uso
useCallback
.Plano medio
En muchos casos, las funciones en línea son muy cómodas de usar y absolutamente buenas en términos de requisitos de rendimiento. Desafortunadamente, esta regla no puede limitarse solo a tipos de componentes memorizados. Si aún desea usarlo en todos los ámbitos, puede, por ejemplo, deshabilitarlo para nodos DOM simples:
fuente