¿Cómo puedo identificar más fácilmente los cuellos de botella en el rendimiento de procesamiento de React?
Tengo un problema con la identificación de cuellos de botella en el rendimiento de procesamiento mientras trabajo en un visor JSON. Con pocos elementos, funciona bien, pero en cierto punto se vuelve molestamente lento.
Al revisar el generador de perfiles, parece que los elementos se procesan lo suficientemente rápido, pero noté algunos problemas que no estoy seguro de cómo solucionar.
Visión general
- La aplicación es un visor JSON que le permite expandir/minimizar todos los elementos a la vez, así como elementos individuales.
- El rendimiento está bien con pocos elementos, pero parece disminuir drásticamente a medida que aumenta la cantidad de elementos.
- Al perfilar tanto mi método de filtro de objetos
performance.now()
como al verificar el tiempo de procesamiento en React DevTools, las cifras parecen estar bien. Podría estar interpretándolo mal. - Intenté usar
React.memo()
elementos sin estado (en particular, la clave/valor, que es el componente representado con más frecuencia), pero no parece mejorar el rendimiento notablemente. Es cierto que no estoy seguro de entender lo suficiente el razonamiento detrás de memorizar los componentes de React para implementar esto de manera útil.
Implementación
- Actualmente, mi aplicación carga datos en un padre que alimenta un componente que carga el árbol JSON usando un elemento recursivo.
- La carga del feed JSON desde la URL cambia el estado del componente principal, que se filtra mediante un método auxiliar que utiliza los valores ingresados en un campo de entrada.
Problemas
Hay dos funcionalidades que reproducen un tiempo de respuesta lento con documentos JSON (no tan grandes):
- El botón expandir todo
- Las primeras pulsaciones de teclas en una consulta de filtro
Con la implementación actual, tanto filtrar como expandir todo desencadena un display: none
cambio en los elementos secundarios, y el comportamiento me lleva a creer que estoy haciendo algo de manera ineficiente para manejar este caso de uso.
Pasos de reproducción
El código está disponible aquí:https://codesandbox.io/s/react-json-view-4z348
Con una compilación de producción aquí (no funciona mejor):https://csb-4z348.vercel.app/
Para reproducir el problema, juegue con la función Expandir todo (el signo más junto a la entrada de filtro) y algunas entradas de filtro.
Luego, intente cargar una fuente JSON con más elementos (puede probar en mi fuente API de GitHub ) e intente filtrar/expandir todo. Observe el gran impacto en el rendimiento.
lo que he notado
- Al iniciar sesión en useEffect, la minimización parece causar ~2 veces más renderizaciones que la expansión total.
- A medida que la entrada del filtro se vuelve más específica, el rendimiento (lógicamente) mejora a medida que se procesan menos elementos.
Pregunta
Si bien agradecería un empujón en la dirección correcta para este caso específico, lo que más me intriga es cómo identificar mejor qué está causando estos problemas de rendimiento.
He investigado la ventana de la salida, pero no es mi primera opción, y estoy bastante seguro de que estoy haciendo algo mal, en lugar de que la causa sea demasiados elementos renderizados.
¡Agradezco su tiempo, y gracias de antemano por cualquier consejo que pueda brindar!
Respuestas
Parece que he respondido a mi propia pregunta. El problema era un problema de reconciliación debido al uso de UUID como accesorio clave en mis componentes secundarios, lo que hacía que se volvieran a renderizar cada vez que cambiaba el estado de minimización. De los documentos:
Las claves deben ser estables, predecibles y únicas. Las claves inestables (como las producidas por Math.random()) harán que muchas instancias de componentes y nodos DOM se vuelvan a crear innecesariamente, lo que puede causar una degradación del rendimiento y pérdida de estado en los componentes secundarios.
Dejaré los pasos aquí para cualquier otra persona que se encuentre con este problema.
Después de (demasiado tiempo) indagar en Performance Profiler, noté que cada vez que minimizaba o expandía los elementos, cada niño se volvía a montar. Después de consultar a Google con una consulta más específica, encontré esta publicación de blog y me di cuenta de que estaba cometiendo este flagrante error de rendimiento.
Una vez que encontré la fuente del problema, encontré muchas otras referencias al mismo.
Después de corregir el accesorio clave, el tiempo de interacción se aceleró un 60 % aproximadamente para minimizar/expandir todo.
Finalmente, memoricé algunos otros componentes relacionados con el filtro instantáneo y finalmente parece estar funcionando tan bien como me gustaría por el momento.
Gracias a cualquiera que haya echado un vistazo a esto mientras tanto, y espero que sea útil para cualquiera que pueda encontrar esto.