Tips de perfomance en Reactjs

Tips de perfomance en Reactjs

        Click here to view this post in English

Aplicando esta tecnica ya se puede tener un gran salto de performance.

Code splitting

Aplicando esta tecnica ya se puede tener un gran salto de performance. La idea detras del code splitting es hacer un lazy load del codigo solo cargandolo cuando lo necesitamos. Por ejemplo si en un modulo de nuestro proyecto usamos una gran libreria de renderizado de pdf, en el login no es necesario renderizar ningun pdf asi que seria mucho mejor no cargarl la libreria pdfjs. En eso se centra esta tecnica y esta basada en estandares de javascript, tiene una sintaxis asi:

javascript import('/modulo.js').then( module => { // hacer algo con el modulo }, error => { // hubo un error al exportar el modulo } )

esto lo podemos llevar a react mediante la libreria React.lazy la cual nos permite cargar componentes bajo demanda. Aca va el truco:


javascript const Mycomponent = React.lazy(() => import('../mymodule'))

En la parte jsx del componente vamos a necesitar usar suspense ya que sino react va a intentar renderizar nuestro componente aunque este no haya sido completamente cargado. React.suspense suspende la renderizacion de nuestro componente hasta que se haya cumplido una condicion y muestra un estado fallback (este fallback puede ser un texto o un div u otro componente).


javascript <div> <React.Suspense fallback={<div>Loading...</div>}> <MyComponent> </React.Suspense> </div>

De esta forma nuestro componente nos muestra en principio un div con un texto que dice Loading... y luego de que la libreria esta completamente cargada muestra MyComponent. Podemos agregarle de forma creativa condicionales para que se muestren ciertos componentes, por ejemplo un checkbox si quiero que se cargue MyComponent o lo que sea, la imaginacion es el limite.

Eager Loading

Esta es una tecnica que nos permite cargar la libreria de forma "apresurada" osea antes de que el usuario la necesite, si de alguna forma detectamos que el usuario tiene una intencion de usar un modulo o cumplir con la condicion para que se cargue la libreria entonces podemos cargarla de antemano para que el usuario no tenga que esperar (nunca queremos que el usuario espere). Ejemplo:


javascript // declaramos una funcion que importe la libreria const loadMyComponent = () => import('../mymodule') const Mycomponent = React.lazy(loadMyComponent)

En nuestro jsx entonces podemos disparar nustra funcion loadMyComponent cuando el usuario pase el mouse por encima de un boton, por ejemplo:


javascript <div onMouseEnter={loadMyComponent} onFocus={loadMyComponent} > <React.Suspense fallback={<div>Loading...</div>}> <MyComponent> </React.Suspense> </div>

El tip detras de esta tecnica es que los navegadores tienen un cache y no importa la cantidad de veces que importemos una libreria solo la va a descargar una vez.
Eager loading es una herramienta y como tal la podemos usar de forma creativa. Solo la imaginacion es el limite.

Webpack Magic Comments

Si usas webpack para construir tu aplicacion, podes usar webpack magic comments, para que webpack pueda agregar instrucciones de precarga al navegador, tambien se pueden hacer cosas como darle nombre a los chunks o seleccionar diferentes modos.


javascript import(/* webpackPrefetch: true */ "./mymodule.js")

esto va a resultar en el head del html algo asi y va a ser cargado en tiempo de inactividadpueden consultar la documentacion de webpack sobre prefetch


javascript <link rel="prefetch" href="my-module-chunk.js">

Por ultimo quiero agregar una observacion sobre el uso de React.suspense, muchas veces solemos anidar toda la aplicacion o muchos componentes dentro del suspense pero esto no siempre es lo recomendable ya que estariamos esperando a que termine la carga del suspense y recien estariamos en condiciones de usar la ui, mientras tanto el navegador nos va a mostrar el fallback que hayamos configurado, esto se traduce en espera para el usuario (nunca queremos que el usuario espere).Lo mismo sucede con el caso contrario, poner un suspense envolviendo cada componente que quiero cargar diferidamente (seria una pesadilla mantenerlo). Mi recomendacion es para que el codigo sea mantenible y funcione fluidamente sin esperas locas, deberiamos tener en cuenta quienes comparten condiciones en comun para poder anidar dentro del suspense y mostrar directamente lo que necesitamos que el usuario vea primero. Al final todo depende del flujo de la aplicacion.

UseMemo

Este hook es una gran ayuda cuando tenemos una gran lista o un valor que es costoso calcular dentro de nuestro componente. Si por ejemplo tenemos una variable de estado y un calculo de area independiente, en ese caso queremos conservar el resultado del calculo pesado asi no tenemos que repetir la ejecucion (el resultado no va a cambiar).


javascript function MyComponent(x,y){ const area = calculateArea(x, y) return( <div> El area es de {area} </div> ) }

Si el componente padre de MyComponent se re renderiza o si agregamos un estado independiente entonces area se volvera a calcular. Para evitar esto vamos a usar el hook React.useMemo que acepta 2 parametros, la funcion a crear y un arreglo de dependencias quedando algo como:


javascript function MyComponent(x,y){ const area = React.useMemo(() => calculateArea(x, y), [x,y]) return( <div> El area es de {area} </div> ) }

Ahora el calculo del area se hara solo en caso de que cambien las variables dependientes.

Workers

Desde IE10 estan soportados los workers en navegadores, permiten ejecutar scripts en segundo plano, javascript por defecto se ejecuta solo en un hilo, usando workers podemos poner a correr tareas costosas en otros hilos y liberar la ejecucion para que siga su curso.No voy a profundizar en este concepto pero sepan que hay un laoder Workerize que puede hacer la vida mucho mas facil para cargar los modulos workerizados.

Memo

La mayoria de interacciones que vamos a tener con nuestra aplicacion van a disparar un re-renderizado, pero no todos los componentes necesitan ser renderizados, si nada cambio para ellos no seria ideal que estuvieran gastando recursos en redibujarse.Para solucionar esta situacion podemos usar React.memo() que permite mantener nua version memoizada del componente que envolvemos.


javascript function MyComponent(){ return( <div> Hola soy un componente </div> ) } const miComponente = React.memo(myComponent);

Tambien podemos pasarle una funcion para comparar manualmente los estados previos y actual para poder decidir que hacer:


javascript function MyComponent(){ return( <div> Hola soy un componente </div> ) } const miComponente = React.memo(myComponent, (prevProps, nextProps) => { // return true });

Podemos retornar true si no queremos que se redibuje por ningun motivo, si retorna false se redibuja. Esto nos da la pauta basica de comparar los estados para saber si queremos redibujar o no:


javascript function MyComponent(){ return( <div> Hola soy un componente </div> ) } const miComponente = React.memo(myComponent, (prevProps, nextProps) => { if(prevProps.props !== nextProps.props) return false });

Como paso final me gustaria recordar que si a React.memo(myComponent) cuando lo uso le paso estados que no hayan variado por defecto no va a redibujarse. Esto nos puede ahorrar escribir muchos comparadores de estados personalizados.

react-virtual performance monitoring... en proceso