Video Summary and Transcription
La charla discute las ramificaciones de las nuevas características concurrentes en React y los conceptos erróneos en torno al modelo de renderizado. Explora los cambios en el proceso de renderizado y la necesidad de manejar el estado cuidadosamente. También destaca los desafíos en la gestión de la comunicación con el mundo exterior y las bibliotecas recomendadas para la sincronización. Menciona los beneficios de usar el modo concurrente en frameworks existentes y las dificultades en la construcción de demos y la aplicación de la inmutabilidad. Por último, enfatiza los beneficios del modo concurrente para componentes pesados.
1. Introducción a las características concurrentes en React
Hola a todos. En esta charla, quiero hablar sobre las ramificaciones e implicaciones de las nuevas características concurrentes en React. También me presentaré y explicaré los dos grupos de desarrolladores con los que nos hemos encontrado. Retrocederemos unos años para comprender la visión de React y cómo ha evolucionado. Las reglas de React no han cambiado.
♪♪ Hola a todos. Espero que estén teniendo una conferencia increíble hasta ahora. Tal vez hayan escuchado algunas charlas o visitado algunos talleres o hayan tenido algunas discusiones sobre las nuevas características concurrentes que se lanzaron en la última versión de React.
En esta charla, quiero hablar sobre exactamente esas características, pero no quiero mostrar las API concretas, sino más bien centrarme en las ramificaciones e implicaciones que esas características tienen en ustedes como desarrolladores de aplicaciones.
Antes de adentrarnos en eso, me gustaría presentarme primero. Mi nombre es Andreas y soy líder de desarrollo en una pequeña agencia en Dresden y nuestro trabajo básicamente consiste en ingresar a los proyectos de otros equipos y ayudarles a mejorar su código, hacer que su software funcione mejor, acelerar su proceso de desarrollo. En este proceso, hablamos con muchos y muchos desarrolladores de diferentes equipos y nos dimos cuenta de que hay más o menos dos grupos de desarrolladores. Un grupo aún no ha oído hablar de las características concurrentes y está haciendo algo incorrecto en la aplicación que podría romperse cuando usen características concurrentes. El otro grupo no está utilizando características concurrentes porque temen que algo pueda romperse en sus aplicaciones. Entonces, muchos de ellos todavía no las están usando realmente.
Y es por eso que hoy quiero contarles un poco sobre esas nuevas características para asegurarme de que puedan mantenerse seguros en un mundo concurrente. Para comenzar, primero tenemos que retroceder unos años cuando Dan Abramov reveló la nueva visión de React. En ese entonces, esta característica todavía se llamaba renderizado asíncrono. El objetivo era adaptarse a la red del usuario y al dispositivo del usuario, para que incluso en dispositivos más pequeños, la aplicación aún pudiera sentirse receptiva. Y cuando el dispositivo es lo suficientemente rápido, todo sucede al instante sin tener ningún tiempo de espera superfluo que no necesiten en sus aplicaciones.
Desde entonces, mucho ha cambiado. Por ejemplo, la fecha de lanzamiento. Como saben, no estamos en 2018 en este momento, así que tuvimos que esperar unos años más. Y muchos otros temas también cambiaron. Por ejemplo, el nombre. En ese entonces, se presentó como renderizado asíncrono o async React. Luego se renombró un poco a modo concurrente o concurrent React, y luego el equipo tomó la increíble decisión de eliminar el modo para que no tengan que optar por toda su aplicación en este nuevo mundo. Pero lanzaron características concurrentes para que ustedes como desarrolladores tengan el control total de cuándo se utiliza el nuevo comportamiento de React en su aplicación y cuándo no.
En ese entonces, incluso lanzaron este artículo de blog donde hicieron algunas preparaciones para esta nueva característica. Por ejemplo, cambiaron el nombre de algunos de los métodos del ciclo de vida como component will mount, component will update, porque ya sabían que estas funciones no eran seguras para el modo concurrente. Tuvieron que hacer ciertas adaptaciones porque sabían a dónde iban, conocían su visión. Así que podrían decir en cierto sentido que React ha cambiado. Pero esto no es cierto. Este es el punto más importante de mi presentación hoy. Las reglas de React no han cambiado.
2. Reglas fundamentales de React y conceptos erróneos
Las reglas fundamentales de React no han cambiado desde 2016. Ahora podemos comenzar a aprovechar estas reglas para mejorar nuestras aplicaciones. Sin embargo, ha habido conceptos erróneos en torno al modelo de renderizado de React, especialmente en lo que respecta a la atomicidad de la fase de reconciliación. En la arquitectura anterior, se garantizaba que la fase de reconciliación fuera atómica. Pero con el lanzamiento de React 16 y la introducción de la arquitectura de fibra, ahora la fase de reconciliación puede interrumpirse. Confiar en la atomicidad de la fase de reconciliación era confiar en detalles de implementación, lo que puede generar problemas.
Las premisas fundamentales y las reglas fundamentales a las que debemos adherirnos como desarrolladores siguen siendo las mismas que en 2016. Por lo tanto, las reglas de React no han cambiado. Recién ahora comenzamos a aprovecharlas realmente. Durante todos estos años, tuvimos que adherirnos a algunos conceptos básicos como la pureza y la ausencia de efectos secundarios sin obtener beneficios reales. Pero ahora, el framework comienza a utilizar realmente el hecho de que cumplimos con estas reglas para mejorar nuestras aplicaciones.
Esto se puede ver en este documento de diseño. Es de hace más de siete años. Aquí se establece que la premisa fundamental de la biblioteca React es que React en sí mismo es una proyección que toma datos como entrada y los transforma en una salida para que su aplicación sea una función pura simple. Con la misma entrada, siempre debería producir la misma salida. Pero como React no utilizaba realmente estas reglas en la aplicación, se han desarrollado algunos conceptos erróneos en torno al modelo de renderizado de React. El primero de ellos es que la fase de reconciliación de React es atómica.
Permítanme explicar qué significa la reconciliación. Cuando React se actualiza, ocurren dos fases. En primer lugar, está la fase de reconciliación. React llama a sus componentes de función, genera esos elementos JSX, los reúne todos juntos y los compara con la versión anterior. Esto es lo que se conoce coloquialmente como DOM virtual. Luego, en la segunda fase, la fase de confirmación, React toma todas esas diferencias y las aplica al DOM real que vemos como usuarios en el navegador. En la arquitectura anterior, se garantizaba que la fase de reconciliación siempre fuera atómica porque se implementaba de esa manera.
En ese entonces, teníamos el reconciliador de pila, lo que significa que React recorría su aplicación, pasaba por sus componentes, comenzaba con el componente de la aplicación y luego el componente de la aplicación renderizaba el componente principal, por lo que el reconciliador pasaba a este componente principal. Teníamos esta pila dentro de la biblioteca React que recorría su aplicación de manera ininterrumpible. Por lo tanto, cuando React comenzaba a trabajar en su componente de la aplicación, solo podía hacer otra cosa una vez que todo lo demás estuviera terminado. Con el lanzamiento de React 16, React cambió esta arquitectura a la arquitectura de fibra. Ya no tenemos esta pila de componentes dentro de nuestra aplicación, pero React mantiene internamente una lista de componentes y puede recorrer esta lista de manera iterativa e incluso interrumpir este proceso. Esto significa que mientras tanto, el grupo de eventos y el navegador tienen cierto margen para manejar eventos de usuario. Por ejemplo, el usuario se desplaza, pero React está realizando algunos cálculos. React puede pausarlos, manejar el evento del usuario, actualizar la interfaz de usuario o hacer algo más, enviar algunas solicitudes o algo similar, pero la fase de reconciliación ahora puede interrumpirse y no ocurrirá de manera atómica. Es muy importante tener en cuenta que nunca se garantizó que la fase de reconciliación fuera atómica. Simplemente sucedió de esa manera debido a la implementación. Confiar en eso significaba confiar en detalles de implementación que ahora generan problemas. Y para eso tengo una pequeña aplicación de demostración.
3. Cambios en el Proceso de Renderizado y Manejo del Estado
Entonces, en esta aplicación de React, el proceso de renderizado se ralentiza artificialmente y la fase de reconciliación ya no es atómica debido a las características concurrentes introducidas en React 18. Esto puede provocar un efecto de desgarro donde diferentes componentes muestran diferentes versiones del estado. Para solucionar esto, debemos dejar de leer valores que pueden cambiar fuera del mundo de React, como variables globales, y en su lugar usar hooks como useState o useReducer. También se deben usar refs para objetos que pueden mutar alrededor de React sin desencadenar una reacción.
Entonces, limpiemos la consola ahora mismo. Lo que tenemos aquí es una aplicación simple de React donde tenemos tres bloques que muestran algún estado. Cada vez que hago clic en este botón de volver a renderizar, se actualiza algún estado y puedes ver en la consola que el renderizado se ralentiza artificialmente. Cada componente tarda aproximadamente cinco segundos en realizar su fase de reconciliación, y luego se puede realizar el commit y podemos ver la actualización en la interfaz de usuario. E incluso con React 18, este proceso sigue siendo atómico por defecto.
Entonces, cuando hago clic en este botón de volver a renderizar y hago clic en el botón de cambiar estado global, simplemente estoy presionando ese botón hasta que se complete el renderizado. Luego podemos ver que la reconciliación acaba de ocurrir como un gran bloque, como una unidad atómica, y solo después de que se haya completado, React puede manejar los clics que se realizan en el botón. Pero ahora, con las características concurrentes, esto ha cambiado. Ahora tengo la posibilidad de programar una actualización de baja prioridad. Así que hagamos clic en ese botón y presionemos el botón de cambiar estado global. Como viste, vimos entre esos dos componentes, el primer bloque se renderizó aquí arriba. Y luego, después de esta fase de reconciliación del primer bloque, React tuvo tiempo o cedió al navegador. Entonces, el navegador pudo informarnos que hubo algunos eventos de clic. Así que React pudo actualizar este estado global. Y luego, después de que eso se maneje, React continuó el proceso de renderizado con los siguientes componentes, es decir, con el segundo bloque y luego con el tercer bloque.
Este proceso es un poco no determinista porque el navegador decide cuándo es el momento de hacer qué, por lo que es un poco difícil mostrarlo realmente. A veces, los cambios ocurrirán entre el segundo y el tercer componente, a veces en ambos, a veces la reconciliación aún ocurre de manera atómica. Pero ahora podemos ver que ya no tenemos esta garantía de que la fase de reconciliación sea atómica. Y ya podemos ver el primer problema. Si nos acercamos un poco aquí, podemos ver que el primer bloque se renderizó con un estado de 26, el segundo bloque se renderizó con un estado de 35 porque en ese tiempo hice clic en el botón 10 veces, por lo que los otros componentes muestran de repente un estado diferente que ya no está actualizado. Así que obtenemos este efecto de desgarro, donde vemos dos versiones diferentes de nuestra aplicación, dos versiones diferentes del estado dentro de la aplicación.
Entonces, necesitamos cambiar algo en nuestro código para evitar que esto suceda. Y lo que debemos hacer es dejar de leer valores que podrían cambiar fuera del mundo de React, es decir, dejar de leer algunos valores que pueden cambiar sin que React pueda saber que cambiaron. Esto significa que ya no podemos leer variables globales que son mutables. Por ejemplo, en mi ejemplo, este estado global es simplemente una variable de nivel de módulo definida con let, y esto se actualiza con el controlador de eventos. Y estamos leyendo esto en el componente, pero React simplemente no puede saber que alguien cambió esta variable simple. Para eso tendríamos que usar el hook useState o useReducer. Entonces, debemos dejar de hacer algo como eso, donde leemos algunos valores globales dentro de nuestro componente porque estos valores podrían cambiar durante la fase de reconciliación porque ya no es atómica. Lo mismo ocurre al leer refs. Los refs están específicamente hechos para objetos que pueden mutar alrededor de React sin que React tenga que reaccionar a eso.
4. Fase de Reconciliación y Fase de Commit
Cuando leemos una referencia durante la fase de reconciliación, podemos encontrarnos con el problema de que la interfaz de usuario muestre diferentes versiones del estado. Esto también se aplica a la lectura del estado global desde una solución personalizada de gestión de estado. La garantía de React de fases de commit atómicas ya no se cumple con la introducción del modo estricto y las características de baja prioridad. La fase de reconciliación ahora puede ocurrir varias veces antes de una fase de commit, lo que puede llevar a inconsistencias potenciales en el estado. Esto se puede ver en nuestra demostración donde el DOM contiene brevemente elementos que luego se eliminan.
Entonces, cada vez que leemos una referencia durante la fase de reconciliación, por ejemplo, dentro del JSX, podemos encontrarnos con el mismo problema de que la interfaz de usuario muestra diferentes versiones del estado. Lo mismo ocurre cuando implementamos nuestra propia solución de gestión de estado personalizada, como Redux en 20 líneas, por ejemplo, y simplemente leemos el estado global desde nuestra solución personalizada de gestión de estado. Este es el mismo problema. Mientras esta solución no esté realmente integrada en React, podemos encontrarnos con problemas.
Vamos a hablar del segundo malentendido. Es que una fase de reconciliación o un renderizado siempre conduce a un commit. Lo que vimos antes es que tenemos esta fase de reconciliación y luego, cuando se completa, tenemos la fase de commit. Solo entonces se actualiza la interfaz de usuario. Incluso si dividimos la fase de reconciliación, todavía tenemos la garantía de React de que la fase de commit es atómica porque React no quiere mostrar diferentes versiones del estado. React quiere mostrar una vista consistente del estado de la aplicación. Por lo tanto, el commit siempre será atómico, pero la reconciliación puede tener pausas en el medio.
Ahora tenemos el problema de que ya no tenemos la garantía de que una fase de reconciliación siempre conduzca a exactamente una fase de commit. Ya escuchaste eso antes en la charla de Nick, donde te dijo que con el modo estricto de React, React renderiza tus componentes dos veces. Por lo tanto, React ya ejecuta tu fase de reconciliación dos veces para una sola fase de commit. Ahora, en el modo estricto, incluso hay dos fases de commit. Pero esto ya rompe la combinación de que siempre tenemos una fase de reconciliación y una fase de commit. También podemos ver eso en nuestra demostración. Ocultemos primero los bloques que tenemos disponibles aquí y marquemos esta casilla de verificación para mostrar los commits. Y ahora vamos a mostrar los bloques. Esto llevará nuevamente 15 segundos, cinco segundos por bloque. Y luego, antes de que esto se complete, vamos a hacer clic en el botón para ocultar los bloques nuevamente. Estoy haciendo clic en Mostrar Bloque y ahora estoy haciendo clic rápidamente en el botón Ocultar Bloques para que suceda lo más rápido posible. Podemos ver la fase de reconciliación ya que acabo de programar una actualización normal. Esto sigue siendo atómico, por lo que todavía pasamos por los tres bloques. El primero, cinco segundos, el segundo, cinco segundos, el tercero, cinco segundos. Y luego ocurre el commit. Por un breve momento, eso no fue realmente visible en la pantalla, pero por un breve momento, el DOM contenía esos tres bloques solo para que React los actualizara una vez más porque hicimos clic en el botón Ocultar Bloques para eliminar completamente los elementos nuevamente. Así que aquí podemos ver que tenemos una fase de reconciliación y una fase de actualización. Todo parece estar bien. Pero ahora, cuando usamos las características de baja prioridad de las características concurrentes de React, hacemos clic en Mostrar Bloques de Baja Prioridad y luego volvemos a hacer clic rápidamente en Ocultar Bloques.
5. Proceso de Reconciliación y Mundo Exterior
React detiene el proceso de reconciliación si determina que los elementos no necesitan ser renderizados. Confiar en que React haga el commit después de la fase de reconciliación puede llevar a errores. Los componentes deben ser funciones puras que no lean ni muten el mundo exterior durante la reconciliación.
Y podemos ver que el proceso se aborta. Solo se reconcilió el primer elemento y luego React se detuvo porque React tuvo tiempo de manejar el evento, el clic en el otro botón que cambió el estado global para ocultar los bloques nuevamente para que React supiera, está bien, ni siquiera necesitamos renderizar esos otros elementos. Abortó por completo este proceso para que los bloques nunca fueran visibles en la interfaz de usuario. Así que tuvimos una fase de reconciliación en el primer bloque sin ningún commit. Entonces, cada vez que confiamos en el hecho de que React hará el commit después de la fase de reconciliación, estamos propensos a errores en nuestras aplicaciones. Eso significa nuevamente que debemos cambiar nuestro comportamiento. Debemos dejar de suscribirnos o mutar el mundo exterior dentro de la función de renderizado, dentro de la fase de reconciliación. Entonces, nuevamente, nuestros componentes deben ser funciones puras que no lean desde el mundo exterior si el mundo exterior puede cambiar y no se supone que mutemos el mundo exterior dentro de la fase de reconciliación.
6. Manejo de la Comunicación con el Mundo Exterior
Existen tres opciones para manejar la comunicación con el mundo exterior en aplicaciones concurrentes de React. La primera opción es mover la comunicación dentro de un gancho UseEffect, asegurando que se devuelva una función de limpieza. La segunda opción es confiar en bibliotecas populares que ya son seguras para el modo concurrente. La tercera opción es utilizar el gancho useSyncExternalStore proporcionado por React, que permite sincronizar la aplicación con datos externos. Al elegir una de estas opciones, podemos asegurarnos de que la interfaz de usuario se mantenga consistente y evitar problemas de desgarro.
Para esto también tengo un par de ejemplos. Por ejemplo, hay algunas bibliotecas por ahí que registran oyentes de eventos o suscriptores a alguna solución de gestión de estado y lo hacen durante el proceso de renderizado. Así que aquí tenemos este componente ficticio que crea un oyente durante la fase de renderizado y lo agrega directamente al objeto window. Ahora imagina que este componente nunca se desmonta, entonces este oyente quedará atrapado en el objeto window y nadie lo eliminará nunca, por lo que tenemos una fuga de memoria en nuestra aplicación. Afortunadamente, este problema no ocurre realmente con demasiada frecuencia en las aplicaciones.
Esto ocurre más en el código de bibliotecas y por lo tanto, cuando te adhieres a bibliotecas populares, no te encontrarás realmente con ese problema, pero aún es importante tenerlo en cuenta, que no mutemos el mundo exterior, no construyamos una conexión, no creemos sockets web y cosas así porque esto podría romperse cuando usamos características concurrentes. Sin embargo, aún debe ser posible de alguna manera hacer estas cosas, leer valores globales, mutar el mundo exterior, porque al final, nuestra aplicación está hecha para comunicarse con un servidor, comunicarse con el usuario, mostrar algún estado cambiante, por lo que debe haber algunas soluciones.
La primera opción es simplemente mover toda esta comunicación dentro de UseEffect porque UseEffect está hecho para que tengamos la construcción, para que podamos hacer una conexión con un oyente de eventos y cosas así, y tenemos la limpieza, por lo que simplemente devolvemos otra función de limpieza desde UseEffect y podemos limpiar si creamos algunas conexiones, agregamos un oyente de eventos y luego lo eliminamos nuevamente cuando el componente se desmonta. Y dado que siempre tenemos esta garantía de que siempre habrá una limpieza después de la llamada a UseEffect, es seguro hacer algo así dentro de UseEffect. Así que siempre tenemos este patrón en el que tienes una variable de estado dentro de tu componente, lees desde el servidor dentro de UseEffect, actualizas la variable de estado, y luego estás listo. El problema con eso es que durante la primera fase de reconciliación, el efecto aún no se ha llamado, por lo que cuando necesites mostrar algo desde la llamada a UseEffect, debes hacer la primera fase de reconciliación sin este valor porque solo estará presente en la segunda fase cuando el estado se haya actualizado.
La segunda opción es elegir una biblioteca que se ajuste a tu caso de uso. Si eliges React Query para comunicarte con el servidor, si eliges algo como Redux Toolkit para manejar tus soluciones de gestión de estado, puedes confiar en las bibliotecas populares que ya son seguras para el modo concurrente porque el equipo de React ha estado trabajando muy de cerca con los mantenedores en este proceso de RFC para que estas bibliotecas ya sean seguras para el modo concurrente. Y la tercera opción es si realmente quieres manejar tu propia solución es usar useSyncExternalStore. Este es un gancho que React nos proporciona que podemos usar para sincronizar nuestra aplicación con algunos datos externos. Para eso, primero debes definir una función de suscripción, pero nuevamente tienes esta noción de construcción, un constructor, por así decirlo, y una limpieza donde debes garantizar que siempre se ejecutarán en pares. Entonces aquí, por ejemplo, agregamos un EventListener al evento global de cambio de tamaño y nos aseguramos de eliminarlo nuevamente una vez que este proceso de sincronización sea limpiado por React. Después de hacer eso, podemos construir un gancho personalizado, use windowWidth, por ejemplo, y usar el gancho useSyncExternalStore donde pasamos nuestra función de suscripción como primer argumento, y el segundo argumento es el valor que lee desde el mundo exterior. Y ahora React puede estar absolutamente seguro de controlar cuándo se llama esta función de lectura para que React pueda estar seguro de que este valor nunca cambia durante un proceso de reconciliación para que la interfaz de usuario siempre se mantenga consistente. De esta manera, podemos usar nuestro gancho personalizado, use windowWidth, donde queramos en nuestra aplicación, y podemos usar el valor durante la representación inicial de nuestro componente y estar seguros de que durante las actualizaciones, también se mantendrá actualizado y no introducirá problemas de desgarro. Eso significa que cuando usamos una de esas tres opciones, podemos relajarnos y dejar que React haga su trabajo porque el equipo de React diseñó React de tal manera que vivimos por encima de este nivel de abstracción. Entonces, en un mundo ideal, nunca nos preguntaríamos cuántas veces se llaman nuestras fases de reconciliación porque React podría tomar algunas decisiones en el futuro para llamarlas más a menudo, llamarlas menos a menudo, omitir algunas o volver a llamarlas. Si React puede confiar en que estas funciones son puras, entonces React puede decidir estas cosas por nosotros y puede hacer optimizaciones sin que nuestro código se rompa. Entonces, con estas suposiciones, ahora es posible volver a relajarse y mantenerse seguro en el mundo concurrente de React. Gracias por escuchar. Muchas gracias. Gracias. Gracias. Muchas gracias. Si quieres unirte aquí, tenemos algunas preguntas.
7. Modelos Mentales Comunes y Manejo del Estado en React
Al trabajar con React, es importante entender que mutar valores fuera de React no es compatible. Los desarrolladores deben cambiar su modelo mental y utilizar ganchos como useState o useReducer para gestionar los cambios de estado. React requiere un control total sobre los cambios de estado para garantizar la consistencia.
Solo un recordatorio para la audiencia, pueden abrir Slido en cualquier momento y hacer preguntas a nuestros oradores hoy. Solo tenemos un par de preguntas hasta ahora, así que por favor, hagan preguntas. Probablemente tendremos tiempo para responderlas. Así que gracias una vez más por su charla. Continuaré con la primera pregunta que tenemos, que es, ¿cuáles son algunos de los modelos mentales comunes que ves en los desarrolladores que llevan a mutar valores fuera de React? Creo que es la forma normal de pensar que, por ejemplo, cuando tienes una variable que puedes usar para mostrar información en la interfaz de usuario. Lo mismo ocurre con las actualizaciones. Tenemos una característica en JavaScript llamada let, y puedes usarla para definir variables que puedes cambiar en el futuro, pero React no lo permite, o React no lo desea y probablemente no lo admita. Así que definitivamente hay algunas curvas de aprendizaje. Es especialmente importante cuando estás introduciendo a desarrolladores de React con otros antecedentes al mundo de React, porque definitivamente tienes que cambiar tu modelo mental para entrar en este mundo donde todo lo que cambia debe ir en un estado de uso o en un reductor de uso, porque solo entonces React tiene el control total sobre eso.
8. Linting Potential Issues in Component Usages
¿Existe alguna forma de analizar problemas potenciales en el uso de componentes? Definitivamente. Ya existen muchas reglas de análisis estático en su lugar, como las matrices de dependencia para los efectos. También existen reglas relacionadas con las mutaciones, como la mutación de matrices u objetos. Las reglas de los hooks son importantes, al igual que las reglas relacionadas con las mutaciones y las variables mutables.
Excelente, gracias. La siguiente pregunta que tenemos, wow, tantos votos a favor, tan rápido. ¿Elon Musk, supongo? No te preocupes, esta es una audiencia encantadora. Solo te harán las preguntas más encantadoras. No te preocupes. ¿Existe alguna forma de analizar problemas potenciales en el uso de componentes? Sí, definitivamente. Y ya existen muchas reglas de análisis estático en su lugar. Por ejemplo, una de las cosas importantes son las matrices de dependencia. Así que cada vez que usamos los efectos, necesitamos agregar esas dependencias para que el efecto siempre se ejecute en los momentos en los que necesitamos que se ejecute. Supongo que también es bastante posible que existan reglas relacionadas con las mutaciones, porque cada vez que mutamos una matriz, mutamos un objeto, ya podemos saber, está bien, esto rompe estas suposiciones sobre el mundo inmutable de React. Así que definitivamente conozco las reglas de los hooks, que son bastante importantes. Y supongo que también debería haber algunas reglas relacionadas con las mutaciones, variables mutables, y cosas así.
9. Recommended Libraries for Synchronization
¿Qué bibliotecas recomendarías para gestionar la sincronización en React? Las opciones populares para la obtención de datos incluyen React Query, que está integrado en muchas otras bibliotecas. Para la gestión del estado, contendientes como Zustand, Yotai y Valtio están diseñados específicamente para manejar los problemas que pueden surgir de las características concurrentes.
Excelente. Muchas gracias. Bien, ¿qué bibliotecas recomendarías? Mencionaste tres soluciones, siendo la opción intermedia el uso de bibliotecas. Sé que dijiste que depende mucho del caso de uso. ¿Existen algunas opciones principales a las que podrías recurrir para gestionar esta sincronización? Creo que las soluciones populares son simplemente para la obtención de data. Algo como React Query. Vimos anteriormente en la charla de TRPC que esto está integrado en muchas otras bibliotecas porque es muy confiable y manejan esos problemas bastante bien. Y para la gestión del estado, están los contendientes habituales como Zustand, Yotai, Valtio, y cosas así, que están diseñados específicamente para eso porque los mantenedores de esas bibliotecas también están bastante conscientes de los problemas que pueden surgir de las características concurrentes. Así que cuando elijas algo que sea popular y utilizado y mantenido activamente, puedes estar seguro de que el equipo ha comunicado con el equipo de React y puedes confiar en eso, creo.
10. Benefits of Concurrent Mode in Existing Frameworks
¿Existen beneficios visibles al utilizar el modo concurrente en frameworks existentes como Gatsby o Next? Es realmente difícil mostrar los beneficios, como se ha demostrado en las discusiones en Twitter. El equipo de Rack cree que habrá beneficios en la programación del proceso de renderizado, pero es difícil proporcionar ejemplos. Las características de división de tiempo son importantes para el renderizado del lado del cliente, especialmente para aplicaciones con gráficos interactivos. Es posible que las páginas estáticas pre-renderizadas con Gatsby no muestren diferencias notables.
Genial. ¿Existen beneficios visibles al utilizar el modo concurrente en frameworks existentes como Gatsby o Next? Me gustaría profundizar en el primer punto de la pregunta. Creo que es realmente, realmente difícil mostrar los beneficios de eso. Ha habido muchas, muchas discusiones en Twitter donde se ha planteado esto, donde algunas personas hicieron algunas demostraciones con algunos cubos girando en un div y donde el framework podría mantenerse con características concurrentes. Y luego otros mantenedores de bibliotecas entran y dicen, nuestra biblioteca es tan rápida que no necesitamos esta concurrencia. Así que es realmente, realmente difícil decir si realmente habrá beneficios tangibles y medibles. El equipo de Rack tiene esta visión de que, dado que es teóricamente posible programar el renderizado del proceso de reconciliación, habrá beneficios allí, pero es realmente difícil mostrar ejemplos allí. Pero en teoría, debería ser bastante claro que siempre que el framework pueda decidir en qué quiere trabajar a continuación, porque algunas cosas son más importantes para el usuario que otras, debería haber algunos beneficios allí, pero nuevamente es realmente difícil mostrarlos. En cuanto a la integración en Next.js y Gatsby, todas esas características son especialmente importantes para, por lo que estas características de división de tiempo son especialmente importantes para el renderizado del lado del cliente. Por lo tanto, cuando tienes aplicaciones que hacen mucho en el lado del cliente, como algunos gráficos interactivos donde puedes filtrar los datos, por ejemplo, se vuelve realmente importante que la programación pueda mostrar realmente los beneficios. Cuando tienes una página estática que está pre-renderizada con Gatsby, por ejemplo, y solo muestra un artículo de blog, probablemente no notarás ninguna diferencia de todos modos.
11. Building Demos and Documentation Challenges
Encontrar difícil construir demos para conceptos abstractos, el equipo de React luchó con la demostración del triángulo de Sierpinski. Se dieron cuenta de que era un ejemplo artificial y se alejaron de él. La documentación y la construcción de aplicaciones de muestra siempre son un desafío.
Sí, genial, gracias. Y sí, encontrar, ya sabes, una de las cosas más difíciles al construir demos para conceptos un poco más conceptuales y abstractos es que pueden ser un poco engreídos, y por lo tanto es difícil... Creo que el equipo de React también luchó con eso porque al principio, tenían esta demostración del triángulo de Sierpinski donde el triángulo se hacía más grande y más pequeño, y rápidamente se dieron cuenta de que esto solo tiene sentido realmente cuando se agregan algunos retrasos artificiales, como en mi demostración, porque entonces se pueden ver los efectos de estas características y se pueden demostrar, pero desde entonces se alejaron de eso porque se dieron cuenta de que estos son ejemplos realmente artificiales, y es realmente difícil imaginar cómo nos afecta en nuestras aplicaciones. Sí, siempre es un desafío con la documentación y la construcción de aplicaciones de muestra.
Enforcing Immutability and Real-World Use Cases
¿Sería justo prohibir la palabra clave 'let' para imponer la inmutabilidad? Trabajar con el modo concurrente añade complejidad adicional, pero es opcional. React puede renderizar dos universos a la vez. Los casos de uso del mundo real para el modo concurrente son raros, pero incluyen la renderización de muchos componentes donde cada uno tarda más de lo habitual, como en gráficos o renderización 3D.
Correcto, tenemos un par de preguntas más. Aún podríamos tener tiempo para un par más, así que si tienen preguntas, no sean tímidos. Entonces, ¿impondrías, o tal vez, considerarías prohibir la palabra clave 'let' para imponer la inmutabilidad? Personalmente, no lo haría. Puedo entender si algunos lo hacen. Hay algunas personas en la community que siempre usan 'let' porque son solo tres letras en lugar de cinco, por lo que es más corto, y creo que es solo una cuestión de preferencia. Cuanto más tiempo trabajas con React, más resaltan esas patterns de mutaciones y actualizaciones como un dedo dolorido. En una revisión de código, se vuelve bastante claro, bastante evidente cuando alguien usa eso, y luego puedes ver si alguien usa 'let', ok, ¿qué está pasando aquí? ¿Está bien o no? Porque hay situaciones en las que esto está totalmente bien perder una variable 'let'. Por ejemplo, si tienes una función en un lado de esa función, en un controlador de eventos, por ejemplo, quieres actualizar una variable, está totalmente bien hacerlo siempre y cuando no mutemos nada que ya esté compartido con React para que React siempre pueda hacer un seguimiento de los data. Tan pronto como compartas algo, entonces debes detener la mutación y con esta precondición, es bastante difícil prohibir completamente algo porque todavía hay casos de uso disponibles para hacer eso.
Sí, absolutamente. Entonces, esta pregunta es, ¿sería justo decir que trabajar con el modo concurrente ahora añade complejidad adicional al trabajar con bibliotecas externas? Creo que esta es una de las principales quejas que se han planteado es que parece que de repente, React se volvió mucho más complejo con eso. En los primeros días, cuando tenías esas garantías que realmente no eran garantías de que todo sucede solo una vez y todo sucede de principio a fin, era más fácil razonar sobre los aspectos internos de React e incluir eso en las características que estabas desarrollando. Y ahora hay esta nueva capa de complejidad. Luego, Abolrov mencionó en Twitter que le gusta pensar que React ahora es capaz de renderizar dos universos a la vez, dos versiones del estado. Y creo que no se me habría ocurrido la idea de usar un marco de interfaz de usuario para renderizar dos universos, lo que creo que muestra bastante bien que sí, se vuelve más complejo, pero solo si realmente queremos aprovechar esas características concurrentes, porque también podemos decidir como equipo simplemente no usarlas, y luego estamos básicamente listos para continuar.
Genial. Esta probablemente sea la última pregunta a la que tendremos tiempo de responder. Y muchas gracias a todos los que enviaron preguntas. Terminamos con algo de excedente, y eso está perfectamente bien. Después de esta sesión de preguntas y respuestas, Andreas estará en la sección de preguntas y respuestas de los oradores en el área de networking. Así que por favor, si hicieron una pregunta para la que no tenemos tiempo, o si piensan en más preguntas en el ínterin, por favor, siéntanse libres de ir allí durante los próximos minutos y preguntarnos directamente. Entonces, esta última pregunta, que creo que es una continuación justa de algunas de nuestras preguntas anteriores, ¿puedes pensar en algún buen caso de uso del mundo real donde el modo concurrente realmente brille? Sí, creo que uno de los, así que tienes que tener un caso de uso donde tengas muchos componentes diferentes y donde cada componente tarde un poco más de lo habitual. Por ejemplo, tienes, para renderizar un frame, tienes 60 milisegundos. Entonces, por ejemplo, podrías decir que tienes como 1,000 componentes y cada uno tarda dos milisegundos. Este es un caso de uso perfecto porque React puede entonces recorrer como ocho componentes por ciclo de renderizado, luego ir al navegador y hacer algunas actualizaciones y luego continuar con este proceso de renderizado. Pero estos casos de uso son realmente raros porque cuando tienes una lista con 1,000 elementos, primero comienzas a virtualizar esa lista para que solo se rendericen los primeros 10 elementos que son visibles. Es por eso que creo que muchos de los demos usan algo como gráficos porque en los gráficos, sucede con bastante frecuencia que tienes como 1,000 puntos de data dentro de un gráfico. Pero luego el problema es, calcular uno de esos puntos de data a menudo es bastante barato. Por lo tanto, no obtenemos realmente los beneficios de eso. Paul Henschel de React 3.0 Fiber siempre muestra algunos demos interesantes porque allí está haciendo muchas cosas con renderizado 3D en el navegador.
Benefits of Concurrent Mode for Heavy Components
Cuando colocas elementos en 3D en un componente, este puede crecer rápidamente en tamaño. Este es un buen caso de uso para las características concurrentes en React, especialmente al tratar con componentes pesados de tamaño mediano.
Y aquí tienes esto, cuando colocas un componente, cuando colocas elementos en 3D en un componente, este componente puede crecer rápidamente en tamaño en un tamaño relevante para el rendimiento. Así que creo que este es un caso de uso bastante bueno. Y por eso él es un gran defensor de esas características porque allí, realmente puedes aprovecharlas porque tienes una cantidad mediana de componentes pesados. Y creo que esta es la situación donde esto brilla más.
Excelente. Muchas gracias. Gracias de mi parte y estoy seguro de parte de la audiencia que te aplaudirá masivamente. Gracias por una charla tan esclarecedora. Muchas gracias por tenerme. Ha sido un placer. Gracias.
Comments