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>