React

1.3 Términos y conceptos más importantes del mundo React

El desarrollo de una aplicación React a veces es muy diferente al de las aplicaciones web tradicionales. Por ejemplo, React sigue un enfoque orientado a componentes para crear una aplicación. Esto significa que una aplicación se compone de numerosos bloques de construcción pequeños que están conectados entre sí de forma flexible. Además, React tiene algunas optimizaciones que permiten que los cambios en la apariencia de la aplicación se muestren de una manera muy eficiente. El objetivo de React es poder manejar grandes cantidades de datos en el frontend.

1.3.1 Componentes y elementos

Como ya se ha mencionado, los elementos centrales de React son los componentes. Son los bloques de construcción de una aplicación y proporcionan la estructura de la interfaz. Un componente debe tener el menor alcance posible e ser independiente de su entorno. Esto permite utilizarlo en varios lugares de la aplicación o incluso en varias aplicaciones. El núcleo de un componente es una función que devuelve la estructura del componente. Esta función se denomina función de renderizado.

Puedes definir la estructura de la pantalla de dos maneras diferentes:

  • Puede utilizar el método React.createElement para crear nuevos elementos y construir una jerarquía con esos elementos.

  • También es posible utilizar JavaScript XML (JSX), una extensión de sintaxis para JavaScript, y así utilizar una notación similar a HTML directamente en el código fuente de JavaScript de su componente para describir la estructura.

La variante más común, que se utiliza a lo largo de este libro, es la segunda, que utiliza JSX.

Un componente no es la unidad más pequeña disponible en React. En el nivel más bajo, están los elementos de React. Estos se traducen en elementos nativos, según el entorno. Para el entorno del navegador, esto significa que el elemento div de React se traduce en un elemento div de HTML. En un componente, puedes usar tanto componentes como elementos para describir la estructura de tu aplicación. Para distinguir entre elementos y componentes, React ha introducido la convención de que los elementos siempre comienzan con una letra minúscula y los componentes con una letra mayúscula.

Los componentes se pueden parametrizar para mejorar su reutilización. Si utiliza JSX, puede pasar atributos a un componente que escriba como etiqueta JSX. En React, se los denomina props y permiten pasar objetos en el árbol de componentes.

En general, los componentes de React tienen un ciclo de vida definido en el que se puede intervenir en varios puntos para influir en el comportamiento de un componente. Los componentes también pueden tener su propio estado. Este state es una estructura de datos que contiene información para representar el componente. Si el valor del estado cambia, React se asegura de que el cambio se refleje en el navegador y el componente se renderizará de nuevo.

Cuando se trata de componentes, React nuevamente distingue entre dos categorías: componentes de clase y de función.

Componentes de la clase

Los componentes de clase de React son, en cierto modo, reliquias de una era pasada. Representaban el estándar en lo que respecta a los componentes antes de la introducción de la API Hooks en la versión 16.8 porque solo los componentes de clase tenían sus propios métodos de estado y ciclo de vida. Sin embargo, la introducción de la API Hooks ha hecho que los componentes de clase pierdan importancia. En las aplicaciones modernas de React, los componentes de clase casi nunca se encuentran.

La razón del declive de los componentes de clase es que el segundo tipo de componentes, los function components, están más en línea con el carácter de React. Son más ligeros y flexibles. Además, la API Hooks resuelve algunos problemas de los componentes de clase. Sin embargo, analizaremos estos aspectos con más detalle en el Capítulo 6.

El nombre de class components se basa en el hecho de que un componente de clase es una clase de JavaScript que deriva de la clase base React.Component. En versiones anteriores de React, los componentes de clase se creaban utilizando el método React.createClass, aunque este método ya no está disponible y solo debes utilizar la variante basada en clase. Alternativamente, puedes utilizar el paquete complementario create-react-class, que es una interfaz que se comporta de manera muy similar a React.createClass.

El núcleo del componente de clase es el método render, que garantiza que el componente se pueda mostrar. El método devuelve un elemento o componente de React como valor de retorno que React utiliza para la representación. React administra el estado de un componente de clase en forma de propiedad y mapea el ciclo de vida a través de varios métodos, como componentDidMount.

Cuando comiences a implementar una nueva aplicación React, debes evitar usar componentes de clase y, en su lugar, recurrir a los componentes de función más modernos.

Componentes de funciones

Como sugiere el nombre, un function component consta de una sola función. Esta función no es más que el método de renderizado de un componente de clase y se encarga de renderizar el componente. De forma predeterminada, un componente de función de este tipo originalmente no tenía sus propios métodos de estado y ciclo de vida. Los componentes de función se usaban en su forma original como simples componentes de visualización. Sin embargo, con la introducción de la API Hooks, esto cambió y los componentes de función se convirtieron en componentes de React completos con estado y ciclo de vida y ahora han reemplazado casi por completo a los componentes de clase. Con algunas excepciones, este libro se centra exclusivamente en los componentes de función para el desarrollo de aplicaciones.

1.3.2 Flujo de datos

Una aplicación React vive de su dinámica. Por lo general, los usuarios interactúan con la aplicación y la utilizan para producir y consumir información. Un elemento central de la creación de una aplicación React es el modelado de flujos de datos. En el árbol de componentes de la aplicación, la información siempre fluye desde los elementos principales hacia los secundarios.

Si imagina una aplicación React típica que se utiliza para gestionar información, normalmente uno de los primeros requisitos es implementar una representación de lista de la información. En el árbol de componentes, la lista en sí está representada por un componente. Las entradas individuales son a su vez componentes que están en una relación padre-hijo con la lista. Las entradas de la lista son los elementos secundarios de la lista (consulte la Figura 1.2). Para mostrar la información, lee los datos en un punto central, como el componente de lista, y distribuye la información a los elementos secundarios individuales. Esta solución tiene la ventaja decisiva de que solo se requiere una solicitud resumida y recibe una descripción general de inmediato.

El componente list contiene el estado, es decir, es responsable de la gestión de datos. Descarga los datos para su visualización desde el servidor y se encarga de enviar los cambios al servidor. Para cada registro de datos, se crea un componente secundario, que recibe la información que se va a representar como props. Este método garantiza que se pueda implementar un flujo de datos dirigido, a través del cual React puede hacer que la representación de la interfaz sea muy eficiente. Un efecto secundario de esta partición de componentes es que puede utilizar los componentes en varios lugares de su aplicación.

Sin embargo, con este tipo de flujo de datos, los componentes secundarios no tienen forma de pasar información de vuelta a sus componentes principales. Pero eso se vuelve necesario cuando la información ha sido modificada y el componente principal debe ser notificado al respecto. Una solución para esto es pasar funciones de devolución de llamada del componente principal al secundario además de la información de representación. Estas funciones, conocidas como event handlers, son ejecutadas por el componente secundario en ciertos casos, por ejemplo, cuando se modifican los datos. La función trabaja en el ámbito del componente principal, por lo que tiene acceso a sus estructuras internas y puede, por ejemplo, modificar el estado. Esto le da la opción de devolver la información al componente principal.

Los beneficios de rendimiento que genera el flujo de datos dirigido provienen del hecho de que React sabe exactamente dónde cambiar la visualización de la aplicación.

1.3.3 El renderizador

En la configuración predeterminada de una aplicación React, se instalan los paquetes react y react-dom. El paquete react contiene la biblioteca en sí. El segundo paquete, react-dom, contiene el renderizador. Este componente se utiliza para traducir elementos React en elementos HTML concretos que luego se pueden renderizar en el navegador.

Otro renderizador para React es React Native, que garantiza que los elementos de una aplicación React se traduzcan a sus equivalentes nativos en una aplicación iOS o Android. Si utiliza distintos renderizadores, debe tener en cuenta que los distintos entornos utilizan sus propios elementos. Por ejemplo, puede utilizar elementos div con react-dom. En React Native, utiliza el elemento view como elemento contenedor.

No solo se puede renderizar una aplicación React en el navegador o en un dispositivo móvil, sino que también hay muchos otros renderizadores disponibles como paquetes NPM. Por ejemplo, Ink es un renderizador para aplicaciones de línea de comandos interactivas que utilizan React.

1.3.4 El reconciliador

Entre otras cosas, el paquete principal de React contiene el reconciliador. Este algoritmo es responsable de detectar cambios en una aplicación. Un ajuste en un componente puede realizarse ya sea modificando los props o el estado. Para que un cambio surta efecto en el navegador, el árbol DOM debe reconstruirse al menos parcialmente en el caso del entorno del navegador. La representación de estructuras HTML sigue siendo uno de los mayores puntos débiles para el rendimiento de las aplicaciones web. React ofrece una solución a este problema en forma de virtual DOM, una imagen de la estructura de la aplicación en forma de objetos JavaScript.

Si se requiere una modificación de la estructura del DOM, React genera una nueva versión del DOM virtual. Esta estructura sirve como modelo para la nueva versión de la aplicación. Luego, React intenta personalizar el DOM existente mediante una serie de acciones optimizadas.

Para optimizar el algoritmo reconciliador se hacen dos supuestos para reducir la complejidad:

  • Dos elementos con diferentes tipos producen árboles diferentes.

  • La propiedad key se puede utilizar para especificar qué elementos secundarios permanecen estables entre dos pasos de renderizado.

Al comparar el estado original y el estado de destino, React procede desde la raíz hacia los elementos secundarios y realiza las comparaciones.

Como ya se ha mencionado, el conciliador comprueba primero si los tipos de dos elementos coinciden. Si los tipos son diferentes, se lleva a cabo la primera optimización del algoritmo, que establece, como se ha mencionado anteriormente, que dos tipos diferentes dan lugar a dos árboles diferentes (véase la Figura 1.3).

En este caso, React puede abortar la comprobación y crear un subárbol completamente nuevo. Esto significa que se descartan todas las estructuras creadas anteriormente y se desenganchan todos los componentes. Esto finaliza el ciclo de vida del componente y, si está presente, ejecuta la lógica de desmontaje adecuada que se utiliza para limpiar el entorno. Luego, se crean los nuevos componentes y se ejecutan las funciones de ciclo de vida adecuadas.

Si los tipos de dos elementos coinciden, se comprueban los atributos de los elementos y solo se ajustan los valores en consecuencia. Por ejemplo, si cambia el nombre de clase o el atributo de style del elemento, el elemento DOM subyacente se ajusta en consecuencia y el subárbol no se descarta (consulte la Figura 1.4).

En el caso de los componentes, la situación es similar a la de los elementos con atributos modificados: la instancia del componente sigue siendo la misma, por lo que se conserva el estado y se ejecutan las funciones de ciclo de vida correspondientes. A continuación, se procesan los elementos secundarios y los componentes.

NoteElementos secundarios múltiples y la propiedad “clave”

En el caso de una lista de varios elementos, puede ocurrir que solo se modifique el orden, pero no los elementos en sí. Esto es relevante, entre otras cosas, cuando se inserta un nuevo elemento en una determinada posición de la lista. Como resultado, cambia el índice de todos los elementos posteriores, lo que significaría que para React tendrían que volver a generarse. Esto se puede evitar mediante la propiedad key. La propiedad key debe contener un valor único y estable en una lista. Si se vuelve a ejecutar el método render del componente padre, se utiliza el valor de la propiedad key para comprobar si los elementos han cambiado.

Normalmente, los identificadores únicos de los registros de datos se utilizan como valor para la propiedad key. El valor no tiene que ser numérico. Sin embargo, es obligatorio que el valor sea único dentro de la lista, no en toda la aplicación, y que no cambie entre las diferentes llamadas de renderizado; de lo contrario, la asignación no se puede realizar correctamente.

Como ayuda durante el desarrollo, React emite una advertencia en cada iteración sobre una lista de elementos sin una propiedad key. Esta advertencia indica que cada elemento secundario de una lista debe tener una propiedad key única. Especialmente para listas grandes, el uso de la propiedad key puede mejorar significativamente el rendimiento de la representación.

Estos conceptos te han dado una primera visión del mundo de React. Los siguientes capítulos de este libro se basan en estos términos y brindan explicaciones detalladas adicionales. En la siguiente sección, aprenderás sobre otras bibliotecas que se usan a menudo junto con React.