¿Cómo se desencadenan las actualizaciones en los componentes funcionales de React?
- 1103Palabras
- 6Minutos
- 21 Sep, 2024
Desde la introducción de los Hooks, los componentes funcionales de React se han convertido en el núcleo del desarrollo moderno de aplicaciones React. En comparación con los componentes de clase, los componentes funcionales no solo son más sencillos, sino que también ofrecen más potencia. Sin embargo, entender el mecanismo de actualización detrás de ellos, especialmente desde una perspectiva del código fuente, nos ayuda a optimizar mejor el rendimiento y evitar renderizados innecesarios. Este artículo profundiza en el mecanismo de actualización de los componentes funcionales partiendo del código fuente de React.
1. Condiciones que desencadenan actualizaciones en componentes funcionales
Las actualizaciones de los componentes funcionales se desencadenan principalmente a través de las siguientes formas:
- Cambio de estado: Al actualizar el estado mediante el método setter de
useState
. - Cambio de props: Cuando las props pasadas por el componente padre cambian, React vuelve a renderizar ese componente.
- Cambio de contexto: Cuando los datos obtenidos mediante
useContext
cambian, React vuelve a renderizar los componentes relacionados.
Desde la perspectiva del código fuente, React almacena el estado del componente, las props y otros datos en un árbol interno llamado árbol Fiber. Cuando estos datos cambian, React entra en la fase de reconciliación para determinar si es necesario volver a renderizar el componente.
2. El rol central del árbol Fiber
Desde la introducción de la arquitectura Fiber en React 16, todas las actualizaciones de los componentes (incluyendo los de clase y los funcionales) se representan como un nodo Fiber. El rol central del árbol Fiber es dividir el proceso de actualización en múltiples pequeñas tareas en lugar de hacerlo todo de una vez, permitiendo que React mantenga la capacidad de respuesta de la interfaz de usuario mientras procesa tareas largas.
Cada componente funcional tiene un objeto Fiber correspondiente que almacena su estado, props y la cola de actualizaciones. Cuando se llama a setState
o al método setter de useState
, React almacena la actualización en la cola de actualizaciones del nodo Fiber correspondiente, esperando ser procesada por el programador de tareas.
3. Proceso de programación de actualizaciones en React
Cuando el estado o las props de un componente funcional cambian, React entra en el proceso de programación de actualizaciones. El flujo principal es el siguiente:
3.1 Mecanismo de actualización de setState y useState
Cuando se llama al método setter de useState
(como setState
), en realidad se crea un objeto de actualización, que contiene el nuevo valor del estado y una referencia al componente que necesita ser actualizado. Este objeto de actualización se agrega a la cola de actualizaciones del nodo Fiber actual, esperando ser programado por React.
1// Implementación simplificada de useState2function useState(initialState) {3 const hook = getHook(); // Obtiene el estado del hook desde el nodo Fiber actual4 if (!hook) {5 // Inicializa el estado del hook6 return mountState(initialState);7 }8 return updateState(hook);9}
En cada actualización, React ejecuta la lógica de actualización en cada nodo Fiber del árbol Fiber. Utiliza la función beginWork
para verificar la cola de actualizaciones, recalcular el valor del estado y desencadenar la re-renderización del componente.
3.2 Almacenamiento y reutilización de Hooks
Durante cada ejecución de un componente funcional, React utiliza una lista interna para almacenar y reutilizar Hooks. Cada llamada a useState
o useEffect
crea o reutiliza un nodo de hook en esta lista para almacenar valores de estado o efectos secundarios.
1function renderWithHooks(currentFiber, nextChildren) {2 currentlyRenderingFiber = currentFiber;3 currentHook = currentFiber.memoizedState; // Obtiene la lista de hooks almacenados previamente4 nextChildren = Component(props); // Vuelve a ejecutar el componente funcional5 return nextChildren;6}
Cada vez que un componente funcional se actualiza, React vuelve a ejecutar el cuerpo de la función desde el principio, pero dado que cada Hook se almacena secuencialmente, puede acceder a los valores de estado y efectos secundarios en el mismo orden, manteniendo la consistencia.
4. Algoritmo de reconciliación y funcionamiento del DOM virtual
4.1 Algoritmo Diff del DOM virtual
El mecanismo de actualización de React depende del algoritmo de reconciliación para determinar qué partes deben actualizarse. Los pasos principales de la reconciliación incluyen:
- Crear un nuevo DOM virtual: Cada vez que el componente se actualiza, React genera un nuevo árbol de DOM virtual basado en el nuevo estado y props.
- Fase de Diffing: React compara los árboles de DOM virtuales nuevo y antiguo utilizando el algoritmo Diff para identificar las partes que necesitan cambiarse.
- Actualizar el DOM real: Una vez que se minimizan las diferencias, React actualiza el DOM real en lotes.
El árbol Fiber permite que React pause y retome entre unidades de trabajo (pasos de actualización), optimizando la ejecución de tareas largas y mejorando la capacidad de respuesta de la aplicación.
4.2 Prioridad de actualizaciones y programación
React utiliza una cola de prioridades para controlar el orden de programación de las actualizaciones. Cada vez que cambian el estado o las props, React asigna una prioridad a la tarea de actualización según su urgencia y decide cuándo ejecutarla.
- Actualizaciones de alta prioridad (como eventos de entrada del usuario) se ejecutan de inmediato.
- Actualizaciones de baja prioridad (como animaciones o la carga de datos en segundo plano) se retrasan para mantener la fluidez de la interfaz de usuario.
5. Optimización del rendimiento en componentes funcionales: useMemo y useCallback
Dado que cada actualización del componente ejecuta de nuevo toda la función, es fundamental utilizar useMemo
y useCallback
para almacenar en caché cálculos costosos o referencias de funciones.
useMemo
: Se utiliza para almacenar en caché los resultados de cálculos y evitar que se recalculen en cada renderizado.useCallback
: Se utiliza para almacenar en caché funciones y evitar que se creen nuevas referencias de función en cada renderizado.
1const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);2
3const memoizedCallback = useCallback(() => {4 handleClick(a);5}, [a]);
Estos Hooks permiten almacenar en caché valores o funciones cuando las dependencias no cambian, reduciendo los cálculos innecesarios y mejorando el rendimiento de la aplicación.
6. Simulación del ciclo de vida en componentes funcionales
Los componentes funcionales no tienen métodos de ciclo de vida como los componentes de clase, pero con useEffect
podemos lograr un comportamiento similar:
componentDidMount
ycomponentDidUpdate
: Se simulan utilizandouseEffect
, que se ejecuta cuando el componente se monta o actualiza.componentWillUnmount
: Se simula devolviendo una función de limpieza dentro deuseEffect
.
1useEffect(() => {2 // Se ejecuta cuando el componente se monta o actualiza3 return () => {4 // Se ejecuta cuando el componente se desmonta5 };6}, [dependencies]); // La matriz de dependencias controla cuándo se ejecuta
Conclusión
El mecanismo de actualización de React es un proceso complejo y eficiente, que involucra la gestión de estados, el DOM virtual, el algoritmo de reconciliación y la gestión del ciclo de vida, entre otros aspectos. Comprender estos principios subyacentes ayuda a los desarrolladores a gestionar de manera más efectiva el estado y el rendimiento de los componentes al utilizar React, mejorando así la experiencia del usuario en las aplicaciones.