Descifrando el Modo Concurrente

Rate this content
Bookmark

Con la llegada del modo concurrente en React 18, hablemos de las complejidades detrás de proporcionar APIs declarativas para el renderizado concurrente. Mientras implementaba las APIs del modo concurrente desde cero para Brahmos.js, me encontré con muchos casos de uso y variabilidad que lo convirtieron en uno de los problemas más interesantes de resolver, y aprecio aún más los esfuerzos de React en promover la UI Concurrente. En esta charla veremos qué significa el modo concurrente para una aplicación web, cuáles son las complejidades internas y cómo lo resolví para Brahmos.js.

30 min
25 Oct, 2021

Video Summary and Transcription

Sudhanshu Yadav discute la característica incremental concurrente en React 18 y la necesidad del modo concurrente para proporcionar una mejor experiencia de usuario. El time slicing es el patrón clave que permite las características concurrentes. El renderizado en segundo plano y la unidad de trabajo se utilizan para lograr un renderizado asíncrono y una consistencia eventual. El modo concurrente introduce un nuevo patrón llamado differing para el renderizado inmediato y el ajuste basado en los recursos disponibles. React proporciona APIs para actualizaciones y transiciones diferidas. La implementación de las APIs del modo concurrente puede ser compleja, pero ofrece beneficios como evitar la inanición de actualizaciones y reutilizar el trabajo. La programación y los slots se utilizan para controlar la ejecución y el control dinámico de FPS. El manejo de múltiples transiciones puede ser desafiante, pero las discusiones del grupo de trabajo de React 18 brindan información sobre los esfuerzos del equipo para mejorar la experiencia de usuario.

Available in English

1. Introducción a Concurrent Mode

Short description:

En esta charla, Sudhanshu Yadav discute la característica incremental concurrente en React 18 y la necesidad del modo concurrente para proporcionar una mejor experiencia de usuario. Las características concurrentes permiten una utilización efectiva de los recursos, el renderizado en segundo plano y las API declarativas para controlar la secuencia de renderizado. El patrón clave que permite las características concurrentes es el time slicing, que divide un trabajo grande en unidades y proporciona espacio para que el navegador maneje otras tareas.

En esta charla, hablaré sobre el modo concurrente, más sobre mi modelo mental hacia las características concurrentes. Así que no hay un modo concurrente como tal que llegue en el React 18, es más como una característica concurrente incremental.

Sobre mí, soy Sudhanshu Yadav, trabajo en Proficy como arquitecto de front-end, soy el autor de Brahmos, la biblioteca React, y también he compartido muchas otras herramientas de código abierto. Soy un fanático de los internos y me encanta discutir los detalles internos de las bibliotecas que uso, me gusta hacer teorías al respecto, puedes encontrarme en Twitter o puedes revisar mi proyecto en GitHub.

Antes de comenzar, entendamos por qué necesitamos el modo concurrente, qué significa para una aplicación React o cualquier aplicación en general. La razón más importante por la que existe la característica concurrente es proporcionar una mejor experiencia personal. Muchas bibliotecas se centran en mejorar el rendimiento de la biblioteca en sí, lo cual debería ser una prioridad, pero a veces esas mejoras no son perceptibles para el usuario, y si un usuario no siente que su aplicación es fluida, entonces su aplicación no es fluida. Deberían perseguir que su aplicación sea fluida.

Hay un problema con los patrones actuales que se centran en mejorar la experiencia personal. Tienen un buen contexto en la aplicación, pero no tienen control sobre la fase de renderizado. Debido a esto, esos patrones no pueden utilizar eficazmente los recursos. Con las características concurrentes, se permite utilizar eficazmente todos los recursos disponibles mientras se mantiene la aplicación receptiva. Las características concurrentes también permiten el renderizado en segundo plano y eso habilita muchas cosas como suspender un renderizado, pausarlo, reanudarlo o agrupar renderizados juntos. Muchas cosas más. También elimina una parte difícil de la longitud del usuario, que es orquestar todo el proceso de renderizado. Proporciona API declarativas que pueden ayudarte a construir, definir más el orden, dar una pista a React o a la aplicación como una biblioteca de que esta es la forma en que quiero que sea mi secuencia de renderizado, o si quiero que las mejores cosas obtengan una pre-renderización o cargar algo de forma perezosa. Todas esas cosas se pueden hacer de una manera más declarativa.

Definitivamente, la característica concurrente tiene mucho alcance, pero ¿qué habilita las características concurrentes? Y el primer y más importante patrón, diría yo, es el time slicing. Para entender el time slicing, veamos un árbol de React aquí, todos los nodos, puedes pensar en un componente que tarda un poco en procesar y cómo se produciría una actualización, como cambiar un estado en cualquier componente y luego desencadenará un nuevo renderizado en ese componente que internamente desencadenará un nuevo renderizado de sus hijos y todas estas cosas sucederán de forma asíncrona de una vez. Y una vez que encuentres qué cambios son necesarios para el DOM real, después de conocer esos cambios, se aplican esos cambios al DOM real.

Ahora, veamos cómo se juega en una línea de tiempo de fotogramas. La mayoría de las bibliotecas intentan lograr 60 FPS y eso generalmente significa que tienes un fotograma que dura 16 milisegundos y tienes 16 milisegundos para ejecutar, incluso menos que eso, para hacer todas las cosas de JavaScript. Entonces, si tienes, hay dos tipos de tareas, tarea de JavaScript y tarea del navegador. La tarea de JavaScript sería como el procesamiento del componente, y la tarea del navegador sería como pintar en el navegador o dar una respuesta de la entrada del usuario, animaciones o cualquier cosa. Ahora, si tu JavaScript es grande y se extiende a través de varios fotogramas, ¿qué sucederá si hay una tarea del navegador en el medio? Tendrá que esperar hasta que se termine la tarea de JavaScript. Básicamente, tu aplicación se entrecortará un poco porque habrá caídas de fotogramas. Entonces, con el time slicing, la idea es que tienes un trabajo grande, así que divide ese trabajo grande en unidades de trabajo. Y después, procesa unidad por unidad y después de cada unidad, verifica si el navegador tiene algo que hacer y básicamente da un espacio de respiración para el navegador. Con eso, digamos que si una tarea del navegador llega en el medio, puede encajar fácilmente en tu lado de renderizado, puede encajar fácilmente entre tus tareas de JavaScript.

2. Unit of Work and Background Rendering

Short description:

Entonces intentemos visualizarlo en nuestra aplicación. Procesas una unidad de trabajo, llamada Fiverr en React, y verificas si debe incluirse en el navegador. Si no es así, procesas la siguiente unidad hasta que debas incluir el navegador. Una vez que el navegador ha terminado, continúas procesando unidades hasta que todos los cambios se rendericen y se confirmen. Esto convierte el renderizado sincrónico en renderizado asíncrono. Para mantener la consistencia, se utiliza el renderizado en segundo plano. En lugar de actualizar el árbol actual, se crea un árbol en progreso. El árbol actual se mantiene consistente con el DOM real mientras se procesa en el árbol en progreso. Las actualizaciones se priorizan según su tipo, con las tareas del navegador y las actualizaciones de entrada teniendo la mayor prioridad. React proporciona API para marcar manualmente las actualizaciones como diferidas o sincrónicas. Se logra la consistencia eventual al final.

Entonces intentemos visualizarlo en nuestra aplicación. Así que realizas un cambio de estado y con el cambio de estado llamamos, no lo llamemos un componente, llamémoslo una unidad de trabajo, Fiverr en términos de React. Procesas una unidad de Fiverr y luego verificas si debes incluirla en el navegador, y si no, procesas la siguiente unidad y sigues haciéndolo hasta que no debas incluir el navegador. Tan pronto como debas incluir el navegador, detienes tu procesamiento en ese momento y programar tu próxima unidad de tarea, después de que el navegador haya terminado de hacer sus cosas. Básicamente, te detienes y permites que el navegador haga sus cosas y una vez que el navegador haya terminado, vuelves a procesar las unidades y continuarás hasta que todas las unidades se procesen y una vez que se rendericen todas las cosas, sabrás qué cambios se requieren y confirmas todos esos cambios.

Esto básicamente convierte tu renderizado sincrónico en renderizado asíncrono y es interoperable, como la tarea del navegador puede interoperar o puede haber más variedad de cosas que pueden interoperar. Pero debido a que ahora se ha vuelto asíncrono, hereda el problema de la naturaleza asíncrona, que básicamente significa que pueden ocurrir múltiples cosas al mismo tiempo y si muchas cosas están sucediendo y están sucediendo en tu árbol principal, tu aplicación puede volverse inconsistente. Para resolver eso, tenemos un patrón llamado renderizado en segundo plano. Con el renderizado en segundo plano, digamos que tenemos la misma aplicación y si hay un cambio de estado, en lugar de actualizar el mismo árbol, lo marcamos como el árbol actual, que es una representación de tu DOM real. En lugar de actualizarlo directamente, creamos un nuevo árbol en progreso y realizamos todas esas operaciones, todo ese procesamiento en el árbol en progreso. Así es como sucede: copias tu fibra del árbol actual en progreso, verificas si hay actualizaciones pendientes, si las hay, procesas la fibra, básicamente la renderizas, y cualesquiera que sean los hijos renderizados, los comparas con los hijos existentes en el árbol actual. Si son iguales, los clonas del árbol actual. Si son diferentes, entonces creas una nueva fibra. Y luego procesas tu siguiente fibra y continúas haciendo eso, repitiendo hasta que todo esté terminado. Con esto, obtienes el beneficio de que no estás mutando, no estás afectando el árbol actual. Entonces, tu árbol actual se mantiene consistente con tu DOM real mientras estás haciendo algún trabajo en el árbol en progreso. Solo cuando hayas terminado con todas las cosas, una vez que hayas renderizado, conozcas todos los cambios y confirmes los cambios en el DOM. Ahora tu árbol en progreso es una representación real de tu DOM. Intercambiamos el árbol actual y el árbol en progreso porque el árbol en progreso es una versión más precisa de lo que es el DOM. Y al ser asíncrono por naturaleza, puede haber N número de actualizaciones que ocurran al mismo tiempo. Y cuando hay muchas actualizaciones en una cola, necesitas priorizarlas. Y diferentes tipos de actualizaciones tienen diferentes prioridades. Por ejemplo, las tareas del navegador tienen la mayor prioridad, como una pintura que debe ocurrir primero. Y también dentro del contexto de la aplicación, las actualizaciones en la entrada, que deben actualizar tu estado, o si estás haciendo alguna animación a través de JavaScript, o si estás interactuando en un botón, necesitas dar una respuesta. Todos estos tipos de actualizaciones tienen una prioridad más alta. Y deben actualizarse de forma síncrona porque necesitas dar una respuesta rápida al usuario. Pero las actualizaciones como los tiempos de espera, las llamadas a la API, las cargas perezosas, todas esas tienen una prioridad más baja y está bien si se retrasan un poco. Podemos diferir ese tipo de actualizaciones y ese tipo de actualizaciones también se pueden interrumpir. Y React proporciona una API diferente para marcar manualmente las actualizaciones como diferidas o sincrónicas, como se usa para transiciones, flushing, etc.

Ahora, hay otro patrón que es muy importante para el renderizado de contenido, que es que al final quieres tener una consistencia eventual.

3. Concurrent Mode and Differing in React

Short description:

Para entender esto, tomemos el ejemplo de una interfaz de usuario con una tabla y una barra de entrada para filtrar. Cuando un usuario escribe, la actualización de la entrada debe ser sincrónica, mientras que la actualización de la tabla puede retrasarse. El debouncing es un patrón común, pero con el modo concurrente se introduce un nuevo patrón llamado differing. Esto permite el renderizado inmediato de la tabla mientras se interrumpe para una nueva entrada del usuario. Se ajusta automáticamente según los recursos disponibles. Las transiciones en React proporcionan una forma declarativa de definir actualizaciones diferidas.

Entonces, para entender esto, veamos un ejemplo. Digamos que tenemos una interfaz de usuario donde tenemos una tabla con muchas filas y columnas y esas celdas tardan un tiempo en renderizarse. Y hay una barra de entrada donde puedes escribir y filtrar cosas. Entonces, cada vez que escribes en la entrada, filtras la tabla.

Ahora, mientras un usuario está escribiendo, esa actualización debe ocurrir más rápido porque debes darle retroalimentación al usuario de que se está ingresando algo. Por lo tanto, debe hacerse de forma sincrónica. Pero la actualización de la tabla puede retrasarse. Está bien si el renderizado de la tabla ocurre en unos pocos milisegundos. Es posible que el usuario ni siquiera lo note o que esté bien con eso.

Entonces, existen patrones existentes para resolver esto. Uno de los patrones comunes es el debouncing. Con el debouncing, haces algo después de unos milisegundos y no lo haces hasta ese segundo. Y si haces algo en el medio, simplemente desplazas ese límite. Pero, con el modo concurrente, se introduce un nuevo patrón llamado differing en lugar del debouncing. Entonces, el debouncing no es muy bueno porque solo retrasa el problema. En realidad, no resuelve el problema. Sería como si renderizaras hasta 200 milisegundos. Pero, ¿qué sucederá si un usuario está tratando de escribir en ese momento? Con el differing, no tienes que esperar 200 milisegundos. Puedes comenzar a renderizar tu tabla lo antes posible. Y sigues intentando renderizarla y la interrumpes cada vez que haya una nueva entrada de usuario. Eventualmente, la tabla se vuelve consistente con lo que el usuario ha escrito. Está bien que se retrase un poco allí. Esto permite que las máquinas con más recursos lo hagan rápido. Para las máquinas que no tienen muchos recursos, se retrasará más. Por lo tanto, se ajustará automáticamente según los recursos disponibles.

Ahora, otro patrón si estás siguiendo React, es posible que hayas pasado por transiciones. Y las transiciones, es posible que hayas visto este fragmento de código. Como en los Componentes Funcionales, puedes usar transiciones y obtener el método de inicio de transición. Y las transiciones son principalmente una forma declarativa de definir una actualización diferida.

4. Actualizaciones y Transiciones

Short description:

Es como decir que tu aplicación está en el estado A y quieres moverla al estado B. Y cualquier actualización entre el estado A y B debe agruparse como una transición. Todas las actualizaciones que ocurren en esa transición se agrupan y se ejecutan juntas. Las transiciones pueden interrumpirse por eventos síncronos, por lo que se utilizan tiempos de espera para asegurar que las actualizaciones se ejecuten. Múltiples transiciones dentro de una acción pueden ejecutarse de forma independiente y fusionar elementos en el árbol actual.

Es como decir que tu aplicación está en el estado A y quieres moverla al estado B. Y cualquier actualización entre el estado A y B debe agruparse como una transición. Y no debe bloquear tu navegador, no debe hacer que tu navegador no responda, puede ocurrir como una actualización diferida.

Pero, además, todas las actualizaciones que ocurren en esa transición, como aquí SETI state que está en el mismo componente, onClick podría llamar a SETI state en su componente padre, todas las actualizaciones se agrupan en una transición y se ejecutan juntas. Debido a que las transiciones pueden interrumpirse por eventos síncronos, podría haber una posibilidad de que la transición no ocurra en absoluto.

Por lo tanto, para esa transición también hay tiempos de espera. Puedes proporcionar un tiempo de espera y decir, al menos en estos milisegundos, quieres que tu actualización esté disponible en el DOM. Y cuando llegue a ese tiempo de espera, intenta ejecutarlas de forma síncrona. También puede haber múltiples transiciones dentro de una acción. Como aquí en HandleClick, estamos llamando a la transición A y la transición... comenzar la transición A, comenzar la transición B. Y ambas transiciones pueden ejecutarse de forma independiente. Es como si se ejecutaran en su propio universo. Y una vez que se ejecutan, pueden fusionar elementos en el árbol actual. Además, cuando llamas al inicio de la transición dos veces, estás llamando a las mismas transiciones de inicio. Por lo tanto, todas estas múltiples transiciones, que son iniciadas por el mismo método de inicio de transición, se agrupan juntas. Por lo tanto, no se observa un comportamiento inconsistente. Estado de comportamiento inconsistente.

5. Complejidades en la Implementación de APIs de Contenido

Short description:

Ahora, si bien el modo de contenido es excelente y ofrece muchas posibilidades, implementar esas APIs no es muy sencillo. La primera complejidad es que tus actualizaciones pueden volverse obsoletas. Para solucionar esto, estoy utilizando un ritmo de lapso de tiempo en esas actualizaciones. Para la parte de mutación, creamos una cola de actualizaciones donde se almacenan todas las actualizaciones. Estas actualizaciones se aplican de forma perezosa durante la fase de renderizado, por lo que solo pueden ocurrir en tu árbol de trabajo en progreso. No tiene que afectar tus referencias de árbol actuales.

Ahora, si bien el modo de contenido es excelente y ofrece muchas posibilidades, implementar esas APIs no es muy sencillo. Hay muchas complejidades y en la próxima parte discutiremos esas complejidades. Aquí, será más sobre mi experiencia en la construcción de esas APIs de contenido para Promise. Pero sí, la mayoría de ellas también se relacionan con React.

La primera complejidad es que tus actualizaciones pueden volverse obsoletas. Debido a que la actualización diferida ocurre de forma asíncrona, puede ser interrumpida por una actualización sincrónica y esos cambios de estado de la actualización diferida pueden volverse obsoletos. De manera similar, también puede volverse obsoleto por otra actualización diferida porque todo está sucediendo de forma asíncrona. Y el efecto más grande es que podrían estar mutando la misma referencia y también pueden tener efectos secundarios.

Entonces, para solucionar esto, lo que estoy haciendo es mantener un ritmo de lapso de tiempo en esas actualizaciones. Y cuando estoy en la fase de renderizado, verifico si la actualización es más antigua que la actualización del árbol actual. Si es nueva, sigo el camino más reciente; de lo contrario, descarto las actualizaciones antiguas. Para la parte de mutación, que puede ocurrir principalmente en un componente de clase donde todos los estados de setstate se almacenan en una instancia del componente. En lugar de aplicar el estado en las llamadas de setstate, creamos una cola de actualizaciones donde se almacenan todas las actualizaciones. Estas actualizaciones se aplican de forma perezosa durante la fase de renderizado, por lo que solo pueden ocurrir en tu árbol de trabajo en progreso. No tiene que afectar tus referencias de árbol actuales.

6. Starvación de actualizaciones y reutilización de trabajo

Short description:

Para solucionar la starvación de actualizaciones, se agrega un recuento de reintentos a las actualizaciones de baja prioridad, dándoles más tiempo para las actualizaciones diferidas. Las transiciones se pueden hacer síncronas después de un tiempo de espera. La reutilización del trabajo ya realizado es crucial para evitar desechar el trabajo rehacer. Las actualizaciones en un árbol diferido se pueden reanudar desde donde se quedaron, verificando si están obsoletas y clonando las fibras si es necesario.

El otro problema es la starvación de actualizaciones. Las actualizaciones de baja prioridad pueden ser consumidas por actualizaciones de alta prioridad, por lo que pueden sufrir de starvación, ya que hay muchas actualizaciones de alta prioridad que ocurren con mucha frecuencia, lo que nunca permitirá que la actualización de baja prioridad ocurra. Para solucionar este problema en particular, agregué un recuento de reintentos en las actualizaciones de baja prioridad y, con más reintentos, comienzan a dar más tiempo para las actualizaciones diferidas, para que tengan más espacio, básicamente más tiempo para procesar las fibras en un estado diferido, en un trabajo en progreso.

También, para las transiciones, tener un tiempo de espera ayuda, donde puedes decir que si una transición es escrita por una actualización de alta prioridad, después de un tiempo de espera, se convertirá en una actualización síncrona. Y la pieza más importante es reutilizar el trabajo que ya hemos realizado, tanto como sea posible. Debido a que todo está sucediendo de forma asíncrona, existe la posibilidad de que desechemos el trabajo rehacer y para resolver eso, en su mayoría, hago todas las actualizaciones síncronas directamente en un árbol actual porque debe ser vaciado de forma síncrona y resolver el flujo de trabajo en progreso solo para las actualizaciones diferidas. Entonces, con eso, obtenemos la capacidad de, por ejemplo, si hay una actualización en curso en un árbol diferido, como un árbol de trabajo en progreso. En ese momento, si hay una actualización sincrónica que se activa en un árbol actual, terminarás esa actualización y volverás al árbol de trabajo en progreso. En el árbol de trabajo en progreso, puedes reanudar desde donde comenzaste porque no tienes que desechar nada, solo tienes que verificar si las actualizaciones que has realizado se han vuelto obsoletas o no, si no están obsoletas, puedes mantener esas fibras. Si están obsoletas, puedes clonar las fibras nuevamente desde el árbol actual.

7. Programación y Ranuras para la Ejecución

Short description:

El problema de programación surge al decidir cuándo ceder al navegador. Para abordar esto, implementé un concepto de ranuras, donde cada ranura tiene una duración de 5 milisegundos. Después de cada ranura, cedemos al navegador y programamos la siguiente ranura utilizando un canal de mensajes. Sin embargo, cuando estamos cerca del fotograma, programamos a través de una llamada de devolución de espera y establecemos un tiempo de espera para garantizar una respuesta más rápida. El tamaño de la ranura puede variar según el número de actualizaciones diferidas y el tiempo disponible en un fotograma, lo que permite un control dinámico de los FPS.

El otro problema interesante, y fue una especie de pesadilla para mí, fue la programación. El problema con la programación es que, digamos que si cedes al navegador dos veces, entonces ejecutarás tu código más rápido, pero tu navegador se volverá... Sacrificarás la capacidad de respuesta de un navegador porque consumirás algunos fotogramas. Al mismo tiempo, si cedes demasiado al navegador, mantendrá tu navegador receptivo, pero hará que la ejecución tarde más tiempo. Entonces, ¿cómo sabes cuándo ceder al navegador? Además, una vez que cedes al navegador, ¿cómo sabes que el navegador ha terminado su trabajo para que puedas programar la siguiente unidad de trabajo en consecuencia? El navegador está trabajando en API como isInputPending y la API del programador que pueden resolver este problema, pero hasta que estén disponibles, tuve que encontrar mi propia solución. Lo que se me ocurrió es un concepto de ranuras donde creé ranuras de 5 milisegundos y en esas ranuras, podemos procesar tantas fibras como sea posible. Ahora, después de cada ranura, cedemos al navegador y programamos la siguiente ranura con un canal de mensajes y los canales de mensajes son buenos en términos de que no tienen un tiempo de espera, puedes decir que la mayoría de las técnicas celulares como requestAnimationFrame o no requestAnimationFrame, sino setTimeout y requestIdleCallback, tendrán mayores retrasos entre dos ranuras. Pero el canal de mensajes también puede consumir algunas tareas del navegador, así que lo que hice es que cuando estamos cerca del fotograma y en ese momento, en lugar de programar desde el canal de mensajes, programamos a través de una llamada de devolución de espera y también con un tiempo de espera y el primero que responda, lo tomamos y ignoramos el otro. Y las ranuras no son constantes. El tamaño de la ranura puede aumentar si se vuelve a intentar una actualización diferida varias veces y también el tamaño de la ranura puede reducirse según si tienes suficiente, como si tus ranuras han tomado suficiente tiempo en un fotograma y queda menos tiempo en ese fotograma en particular, por lo que la ranura puede volverse más pequeña para acomodar eso. Y por cierto, una ranura incluso puede volverse más grande que un fotograma, puede abarcar dos fotogramas. La idea detrás de eso es que programáticamente, no programáticamente, dinámicamente podemos controlar, podemos ajustar los FPS en función de la velocidad de ejecución y los recursos disponibles para un usuario.

8. Manejo de Múltiples Transiciones y Complejidades

Short description:

El manejo de múltiples transiciones puede ser una pesadilla, especialmente cuando hay condiciones de riesgo y conflictos entre las actualizaciones. BrahMos adopta un enfoque más simple, ejecutando las transiciones de forma independiente si provienen de diferentes transiciones de inicio, y fusionándolas si comienzan desde el mismo método. Las transiciones anidadas siguen las transiciones hoja, y solo se mantienen las actualizaciones de estado directas del hijo. Hay muchas complejidades, pero explorar las discusiones del grupo de trabajo de React 18 y el documento anterior sobre el modo concurrente proporcionará información valiosa sobre los esfuerzos del equipo para mejorar la experiencia del usuario.

El otro problema interesante, y las transiciones son buenas, pero manejar múltiples transiciones es una pesadilla. La cosa es que, debido a que son sincrónicas por naturaleza, puede haber múltiples transiciones en una cola. Y debido a que son sincrónicas, también pueden tener condiciones de riesgo. Y las actualizaciones en una transición pueden ser anuladas por otra transición.

También, una transición puede estar anidada, por lo que puedes escribir una transición de inicio dentro de una transición de inicio y también puedes componer transiciones. Como tener múltiples transiciones de inicio envueltas en otra transición de inicio. Incluso puedes hacer todo tipo de cosas desagradables con las transiciones.

Y otra cosa que puede suceder es que dos transiciones pueden intentar actualizar el mismo estado. Y ahora, ¿qué hacer, cuál tomará precedencia? En BrahMos, adopté un enfoque más simple para resolver eso. React, en este momento, fusiona todas las transiciones juntas, pero su plan a largo plazo es ejecutar las transiciones individualmente en un carril diferente. En BrahMos, lo que estoy haciendo es que si hay dos transiciones de diferentes transiciones de inicio, siempre se ejecutan de forma independiente. Y si la transición comienza, como múltiples transiciones comienzan desde el mismo método de inicio, siempre se fusionan. Ahora bien, esto podría ser que estés llamando al mismo método de inicio varias veces, o podría ser que estés presionando un botón y hubo un botón anterior presionado, ¿aún no se ha procesado completamente? Ahora has vuelto a presionarlo. La transición anterior y la nueva transición, las actualizaciones se fusionarán. Y esto, si tienes diferentes transiciones, esas transiciones se mantienen en una cola de transiciones y se procesan una por una. En lugar de crear múltiples árboles de trabajo en progreso para intentar procesar cada transición en su propio lugar, lo mantenemos simple, procesando una transición a la vez. Y las transiciones de tiempo de espera pueden moverse de la cola de transiciones a la sincronización de datos. Eso lo manejan los tiempos de espera. Y para las transiciones anidadas, simplemente sigo las transiciones hoja que existen, eso se respetará. La transición principal solo mantendrá las actualizaciones de estado que son hijos directos de esa transición.

Ahora hay muchas más complejidades, y supongo que podría haber explicado más de ellas, pero tenemos tiempo limitado. Te recomendaría encarecidamente que revises las discusiones del grupo de trabajo de React 18. Son minas de oro. Y también el documento, el documento anterior sobre el modo concurrente, en realidad obtendrás muchas ideas. Y después de ver esos documentos y discusiones, comenzarás a apreciar el esfuerzo del equipo de React. Están tratando de resolver un problema importante, que es brindar una mejor experiencia de usuario. Y en un esquema más amplio, eso es lo único que importa. Con esa nota, me gustaría agradecerte y verte a todos. Gracias. Gracias.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

React Advanced Conference 2022React Advanced Conference 2022
25 min
A Guide to React Rendering Behavior
Top Content
React is a library for "rendering" UI from components, but many users find themselves confused about how React rendering actually works. What do terms like "rendering", "reconciliation", "Fibers", and "committing" actually mean? When do renders happen? How does Context affect rendering, and how do libraries like Redux cause updates? In this talk, we'll clear up the confusion and provide a solid foundation for understanding when, why, and how React renders. We'll look at: - What "rendering" actually is - How React queues renders and the standard rendering behavior - How keys and component types are used in rendering - Techniques for optimizing render performance - How context usage affects rendering behavior| - How external libraries tie into React rendering
React Summit Remote Edition 2021React Summit Remote Edition 2021
33 min
Building Better Websites with Remix
Top Content
Remix is a new web framework from the creators of React Router that helps you build better, faster websites through a solid understanding of web fundamentals. Remix takes care of the heavy lifting like server rendering, code splitting, prefetching, and navigation and leaves you with the fun part: building something awesome!
React Advanced Conference 2023React Advanced Conference 2023
33 min
React Compiler - Understanding Idiomatic React (React Forget)
Top Content
React provides a contract to developers- uphold certain rules, and React can efficiently and correctly update the UI. In this talk we'll explore these rules in depth, understanding the reasoning behind them and how they unlock new directions such as automatic memoization. 
React Advanced Conference 2022React Advanced Conference 2022
30 min
Using useEffect Effectively
Top Content
Can useEffect affect your codebase negatively? From fetching data to fighting with imperative APIs, side effects are one of the biggest sources of frustration in web app development. And let’s be honest, putting everything in useEffect hooks doesn’t help much. In this talk, we'll demystify the useEffect hook and get a better understanding of when (and when not) to use it, as well as discover how declarative effects can make effect management more maintainable in even the most complex React apps.
React Summit 2022React Summit 2022
20 min
Routing in React 18 and Beyond
Top Content
Concurrent React and Server Components are changing the way we think about routing, rendering, and fetching in web applications. Next.js recently shared part of its vision to help developers adopt these new React features and take advantage of the benefits they unlock.In this talk, we’ll explore the past, present and future of routing in front-end applications and discuss how new features in React and Next.js can help us architect more performant and feature-rich applications.
React Advanced Conference 2021React Advanced Conference 2021
27 min
(Easier) Interactive Data Visualization in React
Top Content
If you’re building a dashboard, analytics platform, or any web app where you need to give your users insight into their data, you need beautiful, custom, interactive data visualizations in your React app. But building visualizations hand with a low-level library like D3 can be a huge headache, involving lots of wheel-reinventing. In this talk, we’ll see how data viz development can get so much easier thanks to tools like Plot, a high-level dataviz library for quick & easy charting, and Observable, a reactive dataviz prototyping environment, both from the creator of D3. Through live coding examples we’ll explore how React refs let us delegate DOM manipulation for our data visualizations, and how Observable’s embedding functionality lets us easily repurpose community-built visualizations for our own data & use cases. By the end of this talk we’ll know how to get a beautiful, customized, interactive data visualization into our apps with a fraction of the time & effort!

Workshops on related topic

React Summit 2023React Summit 2023
170 min
React Performance Debugging Masterclass
Top Content
Featured WorkshopFree
Ivan’s first attempts at performance debugging were chaotic. He would see a slow interaction, try a random optimization, see that it didn't help, and keep trying other optimizations until he found the right one (or gave up).
Back then, Ivan didn’t know how to use performance devtools well. He would do a recording in Chrome DevTools or React Profiler, poke around it, try clicking random things, and then close it in frustration a few minutes later. Now, Ivan knows exactly where and what to look for. And in this workshop, Ivan will teach you that too.
Here’s how this is going to work. We’ll take a slow app → debug it (using tools like Chrome DevTools, React Profiler, and why-did-you-render) → pinpoint the bottleneck → and then repeat, several times more. We won’t talk about the solutions (in 90% of the cases, it’s just the ol’ regular useMemo() or memo()). But we’ll talk about everything that comes before – and learn how to analyze any React performance problem, step by step.
(Note: This workshop is best suited for engineers who are already familiar with how useMemo() and memo() work – but want to get better at using the performance tools around React. Also, we’ll be covering interaction performance, not load speed, so you won’t hear a word about Lighthouse 🤐)
React Advanced Conference 2021React Advanced Conference 2021
132 min
Concurrent Rendering Adventures in React 18
Top Content
Featured WorkshopFree
With the release of React 18 we finally get the long awaited concurrent rendering. But how is that going to affect your application? What are the benefits of concurrent rendering in React? What do you need to do to switch to concurrent rendering when you upgrade to React 18? And what if you don’t want or can’t use concurrent rendering yet?

There are some behavior changes you need to be aware of! In this workshop we will cover all of those subjects and more.

Join me with your laptop in this interactive workshop. You will see how easy it is to switch to concurrent rendering in your React application. You will learn all about concurrent rendering, SuspenseList, the startTransition API and more.
React Summit Remote Edition 2021React Summit Remote Edition 2021
177 min
React Hooks Tips Only the Pros Know
Top Content
Featured Workshop
The addition of the hooks API to React was quite a major change. Before hooks most components had to be class based. Now, with hooks, these are often much simpler functional components. Hooks can be really simple to use. Almost deceptively simple. Because there are still plenty of ways you can mess up with hooks. And it often turns out there are many ways where you can improve your components a better understanding of how each React hook can be used.You will learn all about the pros and cons of the various hooks. You will learn when to use useState() versus useReducer(). We will look at using useContext() efficiently. You will see when to use useLayoutEffect() and when useEffect() is better.
React Advanced Conference 2021React Advanced Conference 2021
174 min
React, TypeScript, and TDD
Top Content
Featured WorkshopFree
ReactJS is wildly popular and thus wildly supported. TypeScript is increasingly popular, and thus increasingly supported.

The two together? Not as much. Given that they both change quickly, it's hard to find accurate learning materials.

React+TypeScript, with JetBrains IDEs? That three-part combination is the topic of this series. We'll show a little about a lot. Meaning, the key steps to getting productive, in the IDE, for React projects using TypeScript. Along the way we'll show test-driven development and emphasize tips-and-tricks in the IDE.
React Advanced Conference 2021React Advanced Conference 2021
145 min
Web3 Workshop - Building Your First Dapp
Top Content
Featured WorkshopFree
In this workshop, you'll learn how to build your first full stack dapp on the Ethereum blockchain, reading and writing data to the network, and connecting a front end application to the contract you've deployed. By the end of the workshop, you'll understand how to set up a full stack development environment, run a local node, and interact with any smart contract using React, HardHat, and Ethers.js.
React Summit 2023React Summit 2023
151 min
Designing Effective Tests With React Testing Library
Top Content
Featured Workshop
React Testing Library is a great framework for React component tests because there are a lot of questions it answers for you, so you don’t need to worry about those questions. But that doesn’t mean testing is easy. There are still a lot of questions you have to figure out for yourself: How many component tests should you write vs end-to-end tests or lower-level unit tests? How can you test a certain line of code that is tricky to test? And what in the world are you supposed to do about that persistent act() warning?
In this three-hour workshop we’ll introduce React Testing Library along with a mental model for how to think about designing your component tests. This mental model will help you see how to test each bit of logic, whether or not to mock dependencies, and will help improve the design of your components. You’ll walk away with the tools, techniques, and principles you need to implement low-cost, high-value component tests.
Table of contents- The different kinds of React application tests, and where component tests fit in- A mental model for thinking about the inputs and outputs of the components you test- Options for selecting DOM elements to verify and interact with them- The value of mocks and why they shouldn’t be avoided- The challenges with asynchrony in RTL tests and how to handle them
Prerequisites- Familiarity with building applications with React- Basic experience writing automated tests with Jest or another unit testing framework- You do not need any experience with React Testing Library- Machine setup: Node LTS, Yarn