Inicio / Artículos / Dentro de React Fiber: Unidades de trabajo, Render vs Commit y carriles de prioridad

Dentro de React Fiber: Unidades de trabajo, Render vs Commit y carriles de prioridad

Aprende qué es realmente React Fiber, por qué el reconciler del stack antiguo bloqueaba el hilo principal y cómo las unidades de trabajo, las dos fases y los carriles permiten el renderizado concurrente.

3495 palabras

"Fiber" es uno de esos términos de React que se repite con mucha más frecuencia de la que realmente se explica. Los desarrolladores lo escuchan describir como un nuevo motor, un reemplazo para el Virtual DOM o algo relacionado con Hooks, pero ninguna de esas descripciones es del todo correcta. Esta guía construye el concepto desde cero: primero el problema que tenía React antes de la versión 16, luego qué es Fiber, cómo se divide el proceso de renderizado en dos fases y cómo la programación y las prioridades intervienen en todo esto. Al final, deberías poder explicar Fiber con precisión, identificar los conceptos erróneos más comunes y comprender por qué APIs como startTransition dependen de él.

Fiber en una frase

React Fiber es la arquitectura interna de reconciliación que se incluyó con React 16. Su función es hacer que el proceso de renderizado sea controlable. En lugar de tratar una actualización como una tarea única e indivisible, React modela el trabajo en muchas unidades pequeñas que puede procesar una por una y clasificar según su importancia.

Es más fácil ver este cambio si se compara lado a lado. Antes de Fiber, una actualización seguía una línea recta desde el inicio hasta el final:

Before Fiber:

Update
  ↓
Render entire tree
  ↓
Commit changes
  ↓
Done

Con Fiber, aparece una etapa intermedia en la que el trabajo se divide en partes, y esas partes pueden ordenarse, pausarse y reanudarse antes de que nada llegue a la pantalla:

After Fiber:

Update
  ↓
Break work into units
  ↓
Process units
  ↓
Prioritize / pause / resume when appropriate
  ↓
Commit changes

Esa etapa adicional sustenta casi todas las características modernas de renderizado en React. Para entender por qué justificó una reescritura, basta con observar lo que existía antes.

El reconciliador de pila y su punto ciego

Las versiones de React anteriores a 16 utilizaban lo que generalmente se denomina Stack Reconciler. El proceso general ya era conocido: un cambio en el estado provoca una renderización, esta se compara con el resultado anterior y luego se actualiza el DOM.

State Change
    ↓
  Render
    ↓
Reconciliation
    ↓
DOM Updates

El problema era que la reconciliación se realizaba de forma síncrona. El nombre proviene del hecho de que el reconciler recorría el árbol mediante llamadas funcionales recursivas habituales, por lo que el progreso del trabajo se encontraba en la propia pila de llamadas de JavaScript. Una vez que React comenzaba a procesar una actualización, no tenía forma natural de detenerse a mitad de camino, ya que hacerlo significaría deshacer esa pila y perder su posición. Imagine una aplicación de tamaño moderado:

App
│
├── Header
├── Sidebar
├── Dashboard
│   ├── Chart
│   ├── Table
│   └── Statistics
├── Notifications
└── Footer

Si una actualización afecta gran parte de este árbol, React recorrerá los componentes afectados en una sola pasada continua, desde el primero hasta el último, sin devolver nunca el control:

Start rendering
      ↓
  Component A
      ↓
  Component B
      ↓
  Component C
      ↓
  Component D
      ↓
  Component E
      ↓
     ...
      ↓
   Finish

La salida era correcta; el problema era el tiempo. React no podía interrumpir su trabajo y reanudarlo más tarde, por lo que todo lo demás tenía que esperar.

Por qué un renderizado largo perjudica al usuario

JavaScript no dispone de un hilo principal exclusivo para sí mismo. El navegador utiliza el mismo hilo para ejecutar los manejadores de eventos, calcular el diseño, dibujar píxeles y generar fotogramas:

Browser Main Thread
│
├── JavaScript
├── Event Handling
├── Layout
├── Paint
└── Rendering

Cuando un script mantiene ocupado el hilo durante mucho tiempo, todas esas tareas se acumulan en cola detrás de él. Desde el punto de vista del usuario, la cadena de eventos se presenta de esta manera:

Click
 ↓
React starts large update
 ↓
Main thread remains busy
 ↓
Browser can't respond quickly
 ↓
UI feels slow

Un clic se registra con retraso, una tecla se detecta después de un lapso notable y la animación se interrumpe. En una aplicación pequeña, el proceso de renderizado es lo suficientemente breve como para que nadie se dé cuenta. Sin embargo, a medida que las aplicaciones crecen y las interacciones aumentan, un framework necesita poder decidir cuándo se ejecuta el trabajo de renderizado y cuánto de él se procesa a la vez. Esa es la brecha que Fiber fue diseñado para cerrar.

La idea central: trabajar como unidades programables

El modelo mental detrás de Fiber se resume en una sola frase: el renderizado se divide en unidades de trabajo manejables que React puede programar y priorizar.

Bajo el modelo anterior, la instrucción dirigida al reconciler era en realidad una sola orden:

"Render this entire tree."

Bajo Fiber, React puede, en cambio, analizar cada componente por separado y decidir cuál merece atención primero:

"Here is one piece of work."
"Here is another piece."
"Which work should I process first?"

Al utilizar piezas discretas en lugar de una llamada recursiva profunda, React puede detenerse después de cualquier pieza, verificar si hay tareas más importantes y continuar más tarde.

Qué es realmente una fibra

Una fibra es un objeto de JavaScript simple que representa una unidad de trabajo en el árbol interno de React. En la práctica, hay aproximadamente una fibra por componente o elemento que participa en el proceso de reconciliación. Toma este pequeño árbol de componentes:

App
│
├── Header
├── Sidebar
└── Content

Conceptualmente, React mantiene una estructura paralela compuesta por fibras:

Fiber Tree

App Fiber
   │
   ├── Header Fiber
   ├── Sidebar Fiber
   └── Content Fiber

Los objetos Fiber reales contienen mucho más que un nombre. Almacenan sus enlaces a nodos padre, hijo y hermano, el tipo e identidad del componente, las propiedades y estado pendientes, así como los efectos que deben ejecutarse una vez que el trabajo se haya completado. Estos enlaces explícitos son los que permiten a React recorrer el árbol mediante un bucle en lugar de recursión, lo que a su vez hace posible detenerse y reanudar el proceso. Sin embargo, para toda la arquitectura basta con recordar una sola cosa: una Fiber es una unidad del trabajo de renderizado y reconciliación de React.

Fiber no es un reemplazo del Virtual DOM

Una afirmación frecuente es que Fiber “reemplazó al Virtual DOM”. Eso no ocurrió. Ambos abordan preguntas diferentes.

El Virtual DOM es una descripción de cómo debería verse la interfaz de usuario, que React compara con la descripción anterior para determinar qué debe cambiar:

Virtual DOM
    ↓
"What should the UI look like?"

Fiber es el mecanismo que React utiliza para organizar y ejecutar las tareas necesarias para alcanzar ese objetivo:

Fiber
    ↓
"How should React organize and process the work required to get there?"

Trabajan juntos, pero uno es una representación de la interfaz de usuario y el otro es una forma de procesar las tareas. Si desea ver más de cerca este aspecto comparativo, consulte cómo el sistema de diferenciación del Virtual DOM de React decide qué actualizar.

El árbol de fibras durante una actualización

React mantiene su árbol de fibras durante toda la vida útil de la aplicación. Un ejemplo un poco más detallado muestra cómo un subárbol se anida dentro de él:

                 App
                  │
        ┌─────────┼─────────┐
        ↓         ↓         ↓
      Header    Sidebar    Content
                            │
                     ┌──────┴──────┐
                     ↓             ↓
                   Chart          Table

Cuando cambian las props o el estado, React utiliza esta estructura para encontrar las ramas que necesitan ser procesadas y omitir el resto.

Hacer que la renderización sea interrumpible

La consecuencia más importante de este diseño es que se puede pausar la renderización. Considere una actualización que se divide en varios bloques de trabajo:

Large Update
     ↓
   Work 1
     ↓
   Work 2
     ↓
   Work 3
     ↓
   Work 4

Con el reconciliador síncrono, los cuatro bloques tenían que ejecutarse uno tras otro. Con Fiber, React puede procesar algunos de ellos, ceder el control para que el navegador responda a las entradas o dibuje un frame, y luego continuar:

Work 1
  ↓
Work 2
  ↓
Pause
  ↓
Browser gets an opportunity to handle other work
  ↓
Resume
  ↓
Work 3
  ↓
Work 4

Esto no es una optimización de velocidad en sí mismo; el trabajo total permanece igual. Simplemente, ya no monopoliza el hilo, lo que le da a React espacio para programarlo.

Dos fases: determinar los cambios y luego aplicarlos

React moderno no trata una actualización como un único paso desde la renderización hasta el DOM:

Render → DOM

En lugar de eso, cada actualización pasa por dos fases distintas:

             React Update
                  │
                  ↓
             Render Phase
                  │
                  ↓
            Commit Phase
                  │
                  ↓
                 DOM

La fase de renderización calcula la siguiente interfaz de usuario

En la fase de renderizado, React responde a la pregunta de cómo debería verse la interfaz de usuario en este momento. Concretamente, hace lo siguiente:

  • ejecuta las funciones de los componentes y procesa las actualizaciones pendientes
  • construye o reconcilia el árbol de fibra
  • determina qué difiere del árbol actual
  • reúne la lista de cambios que deben aplicarse

En forma de diagrama, se ingresan el árbol anterior y las nuevas actualizaciones, y se obtiene una descripción del trabajo necesario:

Previous Tree
      +
 New Updates
      ↓
Reconciliation
      ↓
   New Work

Dado que nada en esta fase afecta al DOM, puede ser interrumpida en React moderno. React puede calcular una gran parte del siguiente árbol en segundo plano y nadie ve ningún estado intermedio. Esto también explica por qué React espera que el renderizado esté libre de efectos secundarios: bajo renderizado concurrente, un componente puede ser renderizado más de una vez antes de que su resultado sea confirmado, y en modo estricto se invoca deliberadamente la lógica de renderizado dos veces para detectar código que no funcione bajo esa suposición.

La fase de confirmación aplica el resultado

Una vez que se conocen los cambios, React los confirma:

Render Phase
     ↓
Changes determined
     ↓
Commit Phase
     ↓
DOM updated

Es en la fase de confirmación donde ocurren las mutaciones reales: se insertan, actualizan o eliminan nodos del DOM, se asignan referencias y se ejecutan los efectos de layout. Una forma útil de separar estas dos fases es:

Render Phase
"Let's figure out what needs to change."

Commit Phase
"Now apply those changes."

Por qué no se puede pausar la confirmación

Si hacer pausas es tan útil, ¿por qué no hacerlas en todas partes? Porque la pantalla debe mantenerse consistente. Imagine que React se detuviera a mitad de proceso al escribir en el DOM:

Update A
 ↓
DOM partially changed
 ↓
Pause
 ↓
Update B

El usuario vería una mezcla de interfaces antiguas y nuevas que nunca han existido lógicamente. Por eso React separa las tareas: el proceso de aplicar cambios puede interrumpirse, reiniciarse o descartarse, pero el commit aplica un resultado final de una sola vez. Este límite es clave para comprender cómo funciona React en la actualidad.

Programación: no todas las actualizaciones son iguales

Al dividir el trabajo en unidades, React puede determinar qué tareas son más importantes. Algunas actualizaciones son claramente más urgentes que otras. En respuesta a esto:

User clicks a button

es más importante para el usuario en ese momento que esto:

Rendering a large list somewhere else

De la misma manera, esto:

Typing in an input

Debe sentirse instantáneo. Una actualización pesada que ocurre en otra parte de la página no debe hacer que los caracteres se queden rezagados con respecto al teclado. Fiber proporciona la estructura que permite a React analizar tales diferencias y organizar el trabajo en consecuencia.

Vías: cómo React asigna prioridades

Internamente, las etiquetas modernas de React gestionan las actualizaciones mediante vías, que codifican su prioridad. El código de la aplicación casi nunca interactúa directamente con las vías; React las utiliza para decidir qué actualizaciones pendientes procesar en una renderización determinada y cuáles pueden esperar. Una visión simplificada:

                Updates
                   │
       ┌───────────┼───────────┐
       ↓           ↓           ↓
     Urgent      Normal      Deferred
       │           │           │
       ↓           ↓           ↓
    Process      Process     Process
    sooner       normally    later

Es útil mantener separados tres términos relacionados cuando aparecen juntos:

Fiber
 ↓
Represents work

Scheduler
 ↓
Helps coordinate when work should happen

Lanes
 ↓
Represent priority of updates

Fiber describe el trabajo a realizar, el planificador decide cuándo se ejecuta y las vías indican qué tan urgente es cada actualización. La implementación concreta de estos componentes ha cambiado entre las versiones de React, por lo que debe considerarse como un modelo conceptual y no como una descripción del código fuente actual.

Viejo versus nuevo, paso a paso

Antes de Fiber, la reconciliación era síncrona y se basaba en la pila, y una vez que comenzaba simplemente continuaba sin interrupciones:

Update
  ↓
Reconcile
  ↓
Continue
  ↓
Continue
  ↓
Continue
  ↓
Finish

Había poco margen para interrumpir el trabajo o dar prioridad a una actualización sobre otra.

La arquitectura Fiber introduce decisiones de planificación dentro del flujo de procesamiento:

Update
  ↓
Create / schedule work
  ↓
Process Fiber units
  ↓
Prioritize
  ↓
Pause / resume / restart when appropriate
  ↓
Complete render
  ↓
Commit

Al colocar ambos flujos uno al lado del otro, el cambio se vuelve evidente:

BEFORE FIBER:

Component Tree
      ↓
Synchronous Reconciliation
      ↓
Finish Everything
      ↓
   Commit


AFTER FIBER:

Component Tree
      ↓
Fiber Tree
      ↓
Units of Work
      ↓
Prioritize / Schedule
      ↓
    Render
      ↓
    Commit

El objetivo no era que React fuera más rápido, sino que adquiriera control sobre cómo y cuándo se realiza el proceso de renderizado.

Conceptos erróneos comunes

Fiber no agrega hilos

Fiber no distribuye React en múltiples hilos:

React
 ├── Thread 1
 ├── Thread 2
 └── Thread 3

El JavaScript de React sigue ejecutándose en el hilo principal del navegador. Fiber es una forma de programación cooperativa: React cede voluntariamente el control entre unidades de trabajo para que otras tareas puedan ejecutarse. Esto es fundamentalmente diferente de los Web Workers, que realmente ejecutan código en un hilo separado.

Concurrente no significa simultáneo

Fiber es lo que hace posible la renderización concurrente, pero la palabra “concurrente” puede confundirse fácilmente. No significa que React renderice todo al mismo instante. Significa que React puede preparar una actualización sin bloquear la aplicación durante toda la duración de un proceso de renderizado largo. Una secuencia típica:

Low-priority update
       ↓
React starts rendering
       ↓
Higher-priority update arrives
       ↓
React can prioritize the important work
       ↓
Continue / restart lower-priority work

Se puede dejar de lado una renderización de baja prioridad cuando llega algo más urgente, para luego continuarla o reiniciarla desde cero posteriormente. Ese es el mecanismo detrás de muchas de las interacciones más fluidas en React moderno. En cuanto a cómo se aplica esto en un entorno de framework, el artículo sobre pre-renderización parcial y renderización concurrente aborda el enfoque de Next.js.

Fiber no es “un componente a la vez”

También resulta tentador imaginar Fiber como React que renderiza exactamente un componente, luego el siguiente, y así sucesivamente:

Component 1
Component 2
Component 3

Eso es demasiado simple. El proceso de recorrido, agrupamiento y programación de React es más complejo que una lista simple; lo que ofrece Fiber es un modelo de trabajo mucho más detallado que el antiguo enfoque recursivo.

Transiciones en la práctica

Las transiciones son donde la programación de Fiber se vuelve visible en el código de la aplicación. Tomemos un cuadro de búsqueda donde el usuario acaba de escribir:

User types: "rea"

Esa tecla presionada activa dos tipos diferentes de operaciones:

1. Update the input immediately
2. Update a huge search result list

La actualización de la entrada es lo que el usuario observa; reconstruir una larga lista de resultados puede ser costoso. React permite marcar el segundo tipo como no urgente al envolver la actualización de estado en startTransition:

startTransition(() => {
    setSearchResults(results);
});

Los caracteres escritos se tratan como una actualización urgente para que el campo siga siendo reactivo:

User Input
    ↓
Urgent Update
    ↓
Keep UI responsive

La lista de resultados se trata como una transición, la cual React puede renderizar con menor prioridad e interrumpirla si el usuario sigue escribiendo:

Search Results
    ↓
Transition
    ↓
Can be handled with lower priority

Dos observaciones prácticas. Primero, las transiciones no hacen que el trabajo costoso sea más económico; solo evitan que impida actualizaciones urgentes, por lo que una lista lenta aún puede beneficiarse de la memorización o virtualización. Segundo, el estado propio de la entrada debe permanecer fuera de la transición; de lo contrario, la propia escritura se vuelve diferible y el campo parece lento. Todo este planificación no sería posible sin la fase de renderizado interrumpible que proporciona Fiber.

Por qué React necesitaba una nueva base

Antes de Fiber, React ya contaba con la mayor parte de lo que la gente asocia con él:

  • un DOM virtual
  • reconciliación
  • un modelo de componentes
  • actualizaciones eficientes del DOM

El rediseño estaba orientado al futuro, no a arreglar algo roto. Las aplicaciones estaban creciendo simultáneamente en varios aspectos:

Larger
   +
More interactive
   +
More data-driven
   +
More complex

Ese crecimiento significó que React tuvo que preocuparse no solo por lo que cambió, sino también por responder a preguntas como cuándo debería ejecutarse una tarea, qué tan importante es una actualización, si se puede pausar el trabajo, si otra actualización debería pasar por delante en la cola y cómo evitar bloquear las interacciones que más importan a los usuarios. Fiber es la arquitectura que permite responder a esas preguntas.

Un escenario de panel de control

Considere un panel de análisis con varias áreas complejas:

Dashboard
│
├── Navigation
├── Filters
├── Revenue Chart
├── User Chart
├── Large Data Table
└── Notifications

Cuando el usuario cambia un filtro, una implementación ingenua podría volver a renderizar los gráficos y la gran tabla en una sola operación, lo que haría que el control del filtro pareciera lento para responder. Bajo la arquitectura Fiber, la misma interacción fluye a través de tareas priorizadas:

User changes filter
        ↓
Update begins
        ↓
React performs reconciliation
        ↓
Work is represented through Fiber
        ↓
Updates can be prioritized
        ↓
Rendering completes
        ↓
Commit changes

Los gráficos y las tablas aún deben ser recalculados. Lo que mejora es la capacidad de respuesta, ya que React se encarga de esa tarea.

Fiber en comparación con conceptos relacionados

Fiber y el Virtual DOM

El Virtual DOM es una representación de la interfaz de usuario que React utiliza para determinar qué necesita cambiar:

UI State
   ↓
Virtual Representation

Fiber es la arquitectura interna y la estructura de datos a través de las cuales React representa y procesa el trabajo de renderizado:

Component / Element
       ↓
     Fiber
       ↓
Reconciliation + Scheduling

Juntos forman el sistema de renderizado de React:

Virtual DOM
      +
Fiber Architecture
      ↓
React Rendering System

Relacionados, pero no intercambiables.

Fiber y Web Workers

Estos abordan problemas completamente distintos. Fiber se ocupa de:

Rendering
Reconciliation
Scheduling
Prioritization

Por el contrario, un Web Worker se refiere a:

Running JavaScript
outside the main UI execution context

Su estructura es la siguiente, con los cálculos intensivos trasladados fuera del hilo que gestiona la interfaz de usuario:

Main Thread
     │
     ├── UI
     ├── React
     │
     ↓
Web Worker
     │
     ↓
Heavy computation

Fiber nunca traslada el proceso de renderizado de React a un worker. Si tienes tareas que consumen muchos recursos del procesador y no están relacionadas con el renderizado, como la解析 o cálculos numéricos, un worker sigue siendo la herramienta adecuada; el artículo sobre workers dedicados, compartidos y de servicio explica esas opciones en detalle.

Qué significa esto para tu código

En el trabajo diario nunca interactúas directamente con las fibras. Escribes componentes:

function App() {
    return <Dashboard />;
}

y provocas actualizaciones:

setState(newValue);

React convierte todo eso en tareas que se ejecutan mediante fibras en segundo plano. Tu código se encuentra en la parte superior de una cadena de procesamiento cuyas capas inferiores rara vez necesitas tener en cuenta:

Your React Code
      ↓
React APIs
      ↓
Fiber Architecture
      ↓
Reconciliation
      ↓
Scheduling / Prioritization
      ↓
    Commit
      ↓
     DOM

Nunca se crean nodos Fiber a mano. Lo que se necesita es un modelo mental: mantener la lógica de renderizado pura, colocar los efectos secundarios en funciones de efecto o manejadores, y utilizar transiciones para actualizaciones costosas que no son urgentes.

El resumen más breve posible

El antiguo reconciler seguía una regla simple:

"Start rendering → keep going → finish."

Fiber sigue otra diferente:

"Break rendering into work → decide how to schedule it
→ complete the render → commit the result."

Esa diferencia resume por completo el cambio arquitectónico.

Conclusión

El reconciler basado en pila sirvió bien a React durante años. Fiber abordó aplicaciones que ya superaban sus capacidades. La evolución va desde un control limitado:

Old React
   ↓
Synchronous Stack Reconciler
   ↓
Limited control over rendering work

hasta un modelo en el que el trabajo es explícito, planificable y priorizado:

React Fiber
   ↓
Fiber Tree + Units of Work
   ↓
More flexible reconciliation
   ↓
Scheduling + Prioritization
   ↓
Concurrent Rendering Capabilities

Cuando alguien pregunta qué es React Fiber, decir “el nuevo motor de renderizado” subestima su importancia. Una respuesta más precisa es que Fiber es la arquitectura interna de reconciliación de React, la cual modela el renderizado como unidades de trabajo para que React pueda programar, priorizar, interrumpir y reanudar dichas tareas cuando sea apropiado.

Puntos clave

  • Fiber llegó con React 16 y reemplazó al reconciliador recursivo y síncrono Stack Reconciler.
  • Una fibra es un objeto que representa una unidad de trabajo de renderizado, vinculada en un árbol que React puede recorrer y pausar.
  • La fase de renderizado calcula los cambios y puede ser interrumpida; la fase de confirmación los aplica en un solo paso continuo.
  • Las vías de procesamiento y el programador utilizan la estructura de Fiber para dar prioridad a las actualizaciones urgentes sobre las diferidas.
  • Fiber no es ni el Virtual DOM ni el multihilo; se trata de una programación cooperativa en el hilo principal.
  • La renderización y las transiciones concurrentes se basan en esta estructura, pero gestionan tareas costosas en lugar de eliminarlas.
  • Lecturas relacionadas