React shouldComponentUpdate desmitificado

Mientras desarrollaba en React, ¿alguna vez se preguntó cuándo y por qué se ejecuta el método render () de un componente? ¿O cuándo usar métodos de ciclo de vida menos obvios shouldComponentUpdate ()?

Si la respuesta es sí, su aplicación puede tener problemas de rendimiento. Lea detenidamente y podrá solucionarlos fácilmente.

Todo se reduce a cómo funciona React debajo del capó. La gran promesa de React es que es increíblemente rápido en la representación de elementos en una página.

Para hacer esto, React mantiene en memoria dos versiones del DOM:

  • la versión del DOM que se muestra actualmente
  • la próxima versión del DOM que se mostrará

Compara los dos y actualiza el DOM mostrado solo con las partes que han cambiado. Este proceso se llama reconciliación de árboles. La raíz del árbol evaluado para la reconciliación es un componente cuyos accesorios han cambiado.

Excelente. Ahora, ya sea que lo haya planeado o no, su aplicación web sigue la división del contenedor / componentes de presentación hasta cierto punto. Consulte aquí y aquí las definiciones. Esto significa que cada vista compleja en su aplicación está hecha de un componente contenedor que contiene la lógica y tiene muchos componentes solo de visualización como elementos secundarios.

Este es un patrón muy bueno. Sin embargo, si mira más de cerca, significa que cualquier interacción del usuario en la vista afectará al contenedor y activará un renderizado de él y de todos sus elementos secundarios. Supongamos que tiene una lista de elementos con una elegante visualización de texto, imagen y un botón de estrella amarilla "Agregar a favoritos". El modelo mínimo para un elemento de lista podría ser:

product = { imageUrl: '...', title: '...', isFavourite: false }

La lista de favoritos podría provenir de otra fuente de datos. Independientemente, la organización de sus componentes probablemente se parezca a esto:

Se llama al controlador cuando el usuario hace clic y guarda el lado del servidor de información (o persiste en una tienda o lo que sea) y activa un cambio en this.props.elements.

El resultado de un solo clic activa el renderizado del contenedor y de todas las filas de la lista solo para actualizar una casilla de verificación.

Aquí es donde entra en juego shouldComponentUpdate (). Puede decirle a React que no represente filas que no necesitan usar este método.

class ListItem extends Component { shouldComponentUpdate(nextProps, nextState) { return nextProps.isFavourite != this.props.isFavourite; } ... }

Este es un caso concreto: en un proyecto de aplicación de mercado, teníamos una vista de administración de productos para los vendedores. La lista tenía un patrón "cargar más a medida que el usuario se desplaza hacia abajo" y un elemento de acciones en línea "mostrar / ocultar" para establecer la visibilidad de cada producto. Todo estaba bien cuando los vendedores administraban <100 productos en su tablero. Luego, un determinado vendedor comenzó a ingresar y anunciar más de 300 productos ...

Hubo un retraso de ~ 600 ms antes de que la interfaz de usuario se actualizara después de que un usuario hiciera clic en el icono "habilitar / deshabilitar". El retraso fue definitivamente visible para el usuario final. Usando el generador de perfiles de Chrome, vimos que a React le tomó ~ 2ms renderizar una sola fila. Veces 300… llegamos a 600ms. Agregamos las comprobaciones shouldComponentUpdate () para las condiciones adecuadas. El tiempo de procesamiento después de que el usuario haga clic en menos de 10 ms ...

He elaborado un pequeño proyecto que permite reproducir este caso aquí. Ejecútelo y lea los comentarios del código para ver cómo sucede la magia.

Advertencia para los usuarios de Redux

El problema descrito anteriormente puede ocurrir con más frecuencia si está utilizando Redux y vuelve a seleccionar (o bibliotecas de canalizaciones de acciones "basadas en la tienda" similares).

Con Redux y reselect, empuja acciones a la tienda y conecta a los oyentes a los cambios de la tienda, también conocidos como selectores. Los selectores están disponibles globalmente en la aplicación y en una aplicación grande, es bastante fácil que muchos componentes se asignen a los mismos selectores. Los cambios en la tienda pueden desencadenar cambios de accesorios y, por lo tanto, representaciones que son completamente irrelevantes para algunos componentes.

Aquí está el consejo confuso: no use shouldComponentUpdate () para evitar renderizaciones en tales casos. La lógica dentro de shouldComponentUpdate solo debe mirar lo que es relevante para el componente. Nunca debe anticipar los contextos en los que se usa el componente. La razón es solo que su código se volvería rápidamente inmantenible.

Si tiene este tipo de problemas, significa que la estructura de su tienda es incorrecta o que los selectores no son lo suficientemente específicos. Necesitas llegar a una nueva ronda de modelado.

Recomiendo estas increíbles pautas repetitivas. Promueve la encapsulación de la tienda por contenedor de alto nivel con un área global para las estructuras de datos clave que abarcan toda la aplicación. Este es un enfoque bastante seguro para evitar errores de modelado de tiendas.

¡Gracias por leer! Si le gustó, presione el botón de aplaudir a continuación. Ayuda a otras personas a ver la historia.