¿Cómo funciona el algoritmo de reconciliación de React?
Cuando usamos React la función render() crea un árbol de nodos de React, en la siguiente actualización de estado o props, esta misma función render() devolverá un árbol diferente.
El equipo de React necesitó descubrir cómo actualizar de manera eficiente la interfaz de usuario para que coincida con el árbol más reciente. Actualizar una interfaz no es un proceso fácil, la mayoría de las veces, la aplicación no cambia la estructura de la interfaz de usuario por completo en cada renderizado, sino que algún elemento de nuestra página cambia algunos de sus atributos como el color o el texto. En pocas ocasiones sucederá la contraparte en donde todo cambie, como una recarga de página.
Pero, ¿Cómo sabe React cuál es la forma más eficiente para realizar estos cambios?
Respuesta: El algoritmo de reconciliación.
El algoritmo de reconciliación en React tiene como función decidir como se va a volver a entregar un componente. En el navegador, la manipulación de DOM es costosa y requiere mucho tiempo, tanto en montaje como en desmontaje. Parte de lo que hace que React sea muy eficiente es su algoritmo de reconciliación.
Es posible que te sea familiar una estructura de datos llamada árbol, incluso si no lo estás, puede que trabajes con esta a diario. Un árbol es una colección de nodos (componentes), cada nodo puede tener hijos, que a su vez son nodos también. Básicamente, tanto JSX como HTML se pueden describir como árboles.
React utiliza una instantánea, como se encuentra el DOM en un momento preciso, para hacer una comparación y análisis entre el DOM antes de una actualización y el DOM después; actualizar el DOM virtual es mucho más rápido que el DOM real, ya que el navegador no necesita mostrar una visualización del mismo.
¿Cómo resuelve el problema de optimización de renderizado el algoritmo de reconciliación?
El algoritmo de reconciliación compara dos árboles y devuelve un conjunto de operaciones para actualizar el segundo árbol de modo que coincida con el primero.
-
Ahora bien, si un nodo ha cambiado de tipo, por ejemplo, de un elemento H1 a elemento H2, el primero se descarta y el nuevo se renderiza desde cero.
-
Si dos nodos(componentes) en ambos árboles tienen las misma prop “key” y son el mismo nodo, este se reutiliza sin crear uno nuevo.
Usando estos como axiomas, se puede derivar fácilmente el resto del algoritmo. Si los nodos son diferentes, se descarta el anterior y se crea uno nuevo desde cero. Si los nodos son iguales, el algoritmo comparará sus props, les hace los cambios necesarios y continuará analizando los nodos hijos.
Algo importante a aclarar, hacemos mención de la prop “key”, esto se debe a que el algoritmo, en ocasiones, trae consigo algunos resultados no deseados. El atributo "key" se puede usar para marcar si un nodo del DOM es estable o no. Es posible que hayas visto un mensaje en la consola si alguna vez intentaste mapear una colección y devolver un elemento JSX en cada iteración. Esto se debe a que el algoritmo de reconciliación usa las prop “key” para determinar si el contenido de un nodo ha cambiado, no incluir una “key” puede causar algunos comportamientos imprevistos. Por esta razón, las “key” deben ser únicas para que el algoritmo de reconciliación pueda reconocer e identificar qué elementos son estables y qué elementos no.
Ejemplo:
Supongamos que tenemos una lista de productos, que se deriva de una matriz. Si tuviéramos que ingresar un elemento en el medio, esto cambiará todos los índices de los elementos posteriores. En este escenario, si la prop “key“ no es una representación estable y única de cada elemento, React puede terminar mutando un elemento que no deseamos, lo que puede causar errores importantes y provocar una nueva renderización completa, este es el momento donde necesitamos utilizar la prop “key”, si asignamos una “key” única a cada elemento de la lista, el algoritmo los emparejará fácilmente entre árboles y no provocará comportamientos inesperados. Esta es también la razón por la que nunca se debe usar un índice de matriz o un número aleatorio como clave. Si usas un índice y los elementos se reorganizan, sus índices cambiarán y el algoritmo hará coincidir los nodos de manera incorrecta y la interfaz de usuario se comportará de manera completamente impredecible. Por otro lado, si se usan números aleatorios que se generan en cada renderizado, nada se romperá, pero será extremadamente ineficiente, ya que cada nodo se recreará desde cero en cada renderizado.
En resumen el algoritmo de reconciliación refiere al proceso general de actualización del DOM en respuesta a los cambios en el estado de los componentes, este incluye el Algoritmo de diferencias, utilizado para determinar el número mínimo de operaciones requeridas para transformar un árbol en otro como uno de sus pasos, pero también incluye pasos adicionales como desmontar y montar componentes, actualizar propiedades de componentes y manejar casos de error.