sintesis

React

Códigos disponibles en git

¿Por qué?

Desarrollo web de front-end
  • Ineficiencia en ejecución:

    • lento en modificar el arbol DOM y renderizar todos los elementos

  • Ineficiencia en desarrollo:

    • software complejo con gestión de atributos, eventos y elementos del árbol DOM: altas, bajas y modificaciones

¿Qué?

Bibliotecas en JavaScript, sobre las API de los navegadores, encargada de crear interfaces de usuario basadas en:

DOM virtual, para mejorar la eficiencia

Componentes, para desarraollo declarativo

  • patrón para la representación “virtual” de la Interfaz de Usuario, mantenida en memoria, con reconciliación con el DOM “real”: proceso de sincronización entre ambos mediante ReactDOM

    • no actualiza el DOM Real aplicando cambios de uno en uno sino que calcula todos los cambios que se han producido en el Virtual DOM, y los aplica de forma conjunta en el DOM

    • normalmente asociado con elementos de React, que son objetos representando la interfaz de usuario: table, list, footer, form, …​

    • pero también objetos internos llamados “fibers” para mantener información adicional acerca del árbol de componentes mediante React Fiber

      • reimplementación continua del algoritmo central para aumentar su idoneidad en áreas como animación, diseño y gestos, mediante el renderizado incremental: la capacidad de dividir el trabajo de renderizado en partes y distribuirlo en varios fotogramas.

      • se puede insinuar qué elementos secundarios pueden ser estables en diferentes renders con una propiedad key

  • Encapsulación que maneja su propio estado de forma declarativa/funcional y lo convierte en interfaces de usuario complejas

    • MVC Jerárquico:

      • Modelo: datos

      • Vista: Virtual DOM ⇒ Real DOM

      • Controladores: gestión de eventos

virtualDOMvsRealDOM

Proyecto OpenSource desarrollado por Jordan Walkey de Facebook en mayo de 2013 para productos como Whatsapp, Instagram, Facebook, …​

¿Para qué?

  • Declarativo

    • Disfruta de los beneficios del paradigma funcional, solo constantes, sin variables, bucles, historia de ejecución, …​

      • Ayuda a crear interfaces de usuario interactivas de forma sencilla.

      • Diseña vistas simples para cada estado en tu aplicación y se encarga de actualizar y renderizar de manera eficiente los componentes correctos cuando los datos cambien.

      • El código es más predecible, por lo tanto, fácil de depurar.

    • Este enfoque hace posible la API declarativa de React: le dices a React en qué estado quieres que esté la IU, y se hará cargo de llevar el DOM a ese estado.

      • Esto abstrae la manipulación de atributos, manejo de eventos y actualización manual del DOM que de otra manera tendrías que usar para construir tu aplicación.

  • Basado en Componentes

    • La estructura de componentes es sencilla de mantener, facilita la reutilización del código, es eficiente en el desarrollo de software complejo.

    • Escritos en JavaScript y no en plantillas, permitiendo pasar datos de forma sencilla a través de tu aplicación y mantener el estado fuera del DOM

  • Multiplataforma

    • también renderizar desde el servidor usando Node, así como aplicaciones móviles usando React Native

  • Retrocompatible

    • Es retrocompatible, migrar de versiones anteriores a nuevas es bastante sencillo.

¿Cómo?

Instalación

nmp vite
  • es la herramienta oficial para la creación de proyectos en colaboración con WebPack siendo una solución muy pesada

    • cuyo objetivo principal es agrupar archivos JavaScript para su uso en un navegador

  • npx es una herramienta de ejecución de paquetes de npm 5.2+

npx create-react-app <app>
cd <app>
npm start
  • vite es una alternativa más ligera para la creación de proyectos en JavaScript de cualquier índole

npm create vite@latest
> y
> <app>
> react
> react
cd <app>
npm i
npm run dev
  • En ambos casos, la aplicación está configurada para utilizar Babel que permite:

    • utilizar JavaScript moderno y convertirlo a código compatible en navegadores antiguos

    • la creación de plugins específicos para extender JavaScript fuera del estándar, transpilación, como en el caso de JSX. Opciones

  • ejecución del proyecto en modo desarrollo con el navegador en la dirección localhost:3000

Instalación de bibliotecas
  • prop_types necesaria para las propiedades

npm i --save prop-types
  • sass necesaria para los estilos

npm i --save sass
  • styled-components necesaria para los estilos en Javascript

npm i --save styled-components

JavaScript XML (JSX)

Sin JSX

  • La función ReactDOM.createRoot recibe el elemento HTML que hará de contenedor para uno o más componentes

  • La función render retorna un componente de React y recibe el componente que será renderizados por el navegador

    • La función React.createElement retorna un componente y recibe 3 argumentos:

      • un tipo de elemento de HTML

      • las propiedades del elemento

      • el contenido del elemento, otros elementos HTML o tipos primitivos

  • ./src/main.js

'use strict';

import React from 'react';
import ReactDOM from 'react-dom/client';

ReactDOM
  .createRoot(
    document.getElementById('root'))
      .render(
        React.createElement("table", null,
          React.createElement("thead", null,
            React.createElement("tr", null,
              React.createElement("th", null, "Título columna"),
              React.createElement("th", null, "Título columna")
            )
          ),
          React.createElement("tbody", null,
            React.createElement("tr", null,
              React.createElement("td", null, "Valor de Celda"),
              React.createElement("td", null, "Valor de Celda")
            ),
            React.createElement("tr", null,
              React.createElement("td", null, "Valor de Celda"),
              React.createElement("td", null, "Valor de Celda")
            )
          )
        )
  );
  • index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Aplicación React</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>

Con JSX

  • JSX (JavaScript XML) es un preprocesador que transforma el código a JavaScript, transpilador

    • Es una forma de crear código JavaScript de manera más práctica

    • Permite escribir elementos HTML en JavaScript y añadirlos al Virtual DOM sin necesidad de la función createElement()

    • Parece HTML pero no es HTML

  • ./src/main.js

'use strict';

import React from 'react';
import ReactDOM from 'react-dom/client';

ReactDOM
  .createRoot(
    document.getElementById('root'))
      .render(
          <table>
            <thead>
              <tr>
                <th>Título columna</th>
                <th>Título columna</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>Valor de Celda</td>
                <td>Valor de Celda</td>
              </tr>
              <tr>
                <td>Valor de Celda</td>
                <td>Valor de Celda</td>
              </tr>
            </tbody>
          </table>
  );
  • index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Aplicación React</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>
Incorrecto Sobre-estructura
  • Error debido a que render recibe 2 parámetros.

    • tipico error porque no son elementos de HTML (etiqueta abierta/cerrada),

    • son componente React.createElement(…​)

  • Un elemento <div> envuelve varias etiquetas en una pero

    • compilca el comportamiento de los estilos y el rendimiento del código HTML en un árbol sobrecargado de elementos

  • ./src/main.js

'use strict';

import React from 'react';
import ReactDOM from 'react-dom/client';

ReactDOM
  .createRoot(
    document.getElementById('root'))
    .render(
      <h1>TITULO</h1>
      <table>
        <thead>
          <tr>
            <th>Título columna</th>
            <th>Título columna</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>Valor de Celda</td>
            <td>Valor de Celda</td>
          </tr>
          <tr>
            <td>Valor de Celda</td>
            <td>Valor de Celda</td>
          </tr>
        </tbody>
      </table>
  );
  • ./src/main.js

'use strict';

import React from 'react';
import ReactDOM from 'react-dom/client';

ReactDOM
  .createRoot(
    document.getElementById('root'))
    .render(
      <div>
        <h1>TITULO</h1>
        <table>
          <thead>
            <tr>
              <th>Título columna</th>
              <th>Título columna</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>Valor de Celda</td>
              <td>Valor de Celda</td>
            </tr>
            <tr>
              <td>Valor de Celda</td>
              <td>Valor de Celda</td>
            </tr>
          </tbody>
        </table>
      </div>
  );

Componentes

  • JSX es JavaScript y los componentes son exportados como funciones, las funciones en JavaScript solo pueden devolver un tipo de valor

    • esto también se aplica a React, es por eso que un componente solo puede devolver un elemento o componente

    • cuando un componente no tiene hijos pueden utilizarse con la etiqueta de cierre a la derecha

  • ./src/Table/Table.jsx

const Table = () =>
  <table>
    <thead>
      <tr>
        <th>Título columna</th>
        <th>Título columna</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Valor de Celda</td>
        <td>Valor de Celda</td>
      </tr>
      <tr>
        <td>Valor de Celda</td>
        <td>Valor de Celda</td>
      </tr>
    </tbody>
  </table>;

export default Table;
  • Si el fichero del módulo es renombrado a index.jsx, el componente es importado por defecto solo con especificar el nombre del directorio

  • ./src/main.jsx

import ReactDOM from 'react-dom/client';
import Table from './components/Table/Table';

ReactDOM
  .createRoot(
    document.getElementById('root'))
      .render(
          <Table />
      );
  • ./src/main.jsx

import ReactDOM from 'react-dom/client';
import Table from './components/Table';

ReactDOM.createRoot(
  document.getElementById('root'))
    .render(
        <Table />
    );

Composición

  • La composición en React es un patrón de desarrollo basado en el modelo de componentes (modularidad, reusabilidad, reparto de responsabilidades, …​), en el que se construyen componentes a partir de otros componentes más pequeños, para ensamblar una unidad cohesiva más grande.

    • Por convención, los componentes se nombran en formato PascalCase, independientemente de que sean funciones

    • Solo debe exportarse así mismo

      • No se recomienda exportar más de uno para facilitar su reutilización

      • Si es posible definir varios componentes en la especificación de un componente para usarlo de forma privada dentro del mismo

        • Dependerá de la complejidad del mismo

    • Del mismo modo que HTML permite anidar varios elementos bajo un mismo padre, con JSX es posible anidar uno o varios componentes bajo el mismo elemento HTML padre

diagramaComponentes
  • ./src/main.jsx

import ReactDOM from 'react-dom/client';
import Table from './components/Table';

ReactDOM.createRoot(
  document.getElementById('root'))
    .render(
        <Table />
    );
  • ./src/components/Table/index.jsx

import TableBody from "./TableBody";
import TableHead from "./TableHead";

const Table = () =>
  <table>
    <TableHead />
    <TableBody />
  </table>;

export default Table;
  • ./src/components/Table/TableHead.jsx

import TableColumns from "./TableColumns";

const TableHead = () =>
  <thead>
    <TableColumns />
  </thead>;

export default TableHead;
  • ./src/components/Table/TableColumn.jsx

const TableColumns = () =>
  <tr>
    <th>Título columna</th>
    <th>Título columna</th>
  </tr>;

export default TableColumns;
  • ./src/components/Table/TableBody.jsx

import TableRow from "./TableRow";

const TableBody = () =>
  <tbody>
    <TableRow />
    <TableRow />
    <TableRow />
    <TableRow />
  </tbody>;

export default TableBody;
  • ./src/components/Table/TableRow.jsx

const TableRow = () =>
  <tr>
    <td>Valor de Celda</td>
    <td>Valor de Celda</td>
  </tr>;

export default TableRow;

Fragmentos

  • Los Fragmentos permiten agrupar una lista de hijos sin agregar nodos extra al DOM

  • Los Fragments se pueden utilizar de forma abreviada sin necesdad de importación

  • ./src/components/Table/index.jsx

import { Fragment } from "react";
import TableBody from "./TableBody";
import TableHead from "./TableHead";

const Table = () =>
  <Fragment>
    <h1>React Course</h1>
    <table>
      <TableHead />
      <TableBody />
    </table>
  </Fragment>;

export default Table;
  • ./src/components/Table/index.jsx

import TableBody from "./TableBody";
import TableHead from "./TableHead";

const Table = () =>
  <>
    <h1>React Course</h1>
    <table>
      <TableHead />
      <TableBody />
    </table>
  </>;

export default Table;

Modo Estricto

  • React.StrictMode

    • es un componente de React que no es renderizado en el HTML,

    • permite envolver otros componentes para ayudar a crear componentes siguiendo las mejores prácticas, verificar que no se utilizan funciones desaprobadas, efectos secundarios no deseados, …​

    • es posible importar StrictMode directamente de ‘react’.

  • ./src/main.jsx

'use strict';

import { StrictMode } from 'react';
import ReactDOM from 'react-dom/client';
import Table from './components/Table';

ReactDOM.createRoot(
  document.getElementById('root'))
    .render(
      <StrictMode>
        <Table />
      </StrictMode>
    );

Propiedades

  • Son valores que reciben un componente hijo desde uno padre

    • se agrupan en un objeto llamado props,

  • son inmutables, es decir, son valores de sólo lectura que no se pueden modificar

  • por convención, se escriben en formato camelCase

  • para escribir expresiones de Javascript junto con la sintaxis de JSX se utilizan las llaves { <expresión> }

    • una restricción de JSX es que no puedes utilizar if, else, while o for

      • debido a que estas sentencias no devuelven un valor

  • puede recibir diferentes tipos de datos:

  • Strings

  • Numbers

  • Booleans

  • Arrays

  • Objects

  • Functions

  • React Elements, la parte JSX de un componente

  • React Components

  • ./src/main.jsx

'use strict';

import { StrictMode } from 'react';
import ReactDOM from 'react-dom/client';
import Table from './components/Table';

ReactDOM.createRoot(
  document.getElementById('root'))
    .render(
      <StrictMode>
        <Table title="React Course" />
      </StrictMode>
    );
  • ./src/components/Table/index.jsx

import TableBody from "./TableBody";
import TableHead from "./TableHead";

const Table = (props) =>
  <>
    <h1>{ props.title }</h1>
    <table>
      <TableHead />
      <TableBody />
    </table>
  </>;

export default Table;

Propiedades no-HTML

  • Debido a que JSX es JavaScript, existen 2 atributos de HTML que no puede ser usados como propiedades dentro del código JSX, porque colisionan con palabras clave:

  • class, se cambia por className

  • for, se cambia por htmlFor

  • HTML

<div class="mi-clase">
<label for="id">
  • JSX

<div className="mi-clase">
<label htmlFor="id">

Propiedad innerHTML

  • dangerouslySetInnerHTML es un atributo que reemplaza a innerHTML

    • El atributo dangerouslySetInnerHTML recibe un objeto con una propiedad _html cuyo valor será el contenido en _HTML

function createMarkup() {
  return {__html: 'First &middot; Second'};
}

function MyComponent() {
  return <div dangerouslySetInnerHTML={createMarkup()} />;
}
  • A nivel de rendimiento, durante el proceso de reconciliación, React ignorará el elemento que contenga dicho atributo

  • Hay que tener cuidado al utilizarlo, generalmente es un riesgo establecer código HTML directamente desde el código

    • abre la puerta a ataques XSS (Cross-Site-Scripting) por lo que es recomendable es uso de bibliotecas como DOMPurify para evitarlos

    • Hay que evitar el uso de dicho atributo siempre que sea posible

Propiedades por defecto

  • Es posible establecer valores por defecto para cada una de las propiedades que recibe un componente

  • En caso de que el componente no tenga una propiedad definida desde el componente padre, se le asignará el valor por defecto

  • ./src/main.jsx

'use strict';

import { StrictMode } from 'react';
import ReactDOM from 'react-dom/client';
import Table from './components/Table';

ReactDOM.createRoot(
  document.getElementById('root'))
    .render(
      <StrictMode>
        <Table />
      </StrictMode>
    );
  • ./src/components/Table/index.jsx

import TableBody from "./TableBody";
import TableHead from "./TableHead";

const Table = ({ title }) =>
  <>
    <h1>{ title }</h1>
    <table>
      <TableHead />
      <TableBody />
    </table>
  </>;

Table.defaultProps = {
  title: 'Default Title'
}

export default Table;

Precondiciones

  • A medida que una aplicación crece, ésta empieza cada vez más a manejar un mayor volumen de datos y, por tanto, hay un mayor número de componentes, crece la posibilidad de encontrarse con errores de verificación de tipos que afectan al comportamiento de la aplicación.

  • Por ello React incorpora un sistema de validación para las propiedades que reciben los componentes mediante la biblioteca propTypes

    • Requiere instalación particular

    • Es importante remarcar que los incumplimientos especificados mediante propTypes (propiedad obligatoria, …​) no van a interrumpir la ejecución de la aplicación, solo mostrará por consola de manera informativa

      • Warning: Failed prop type: The prop title is marked as required in Table, but its value is undefined.

  • ./src/main.jsx

'use strict';

import { StrictMode } from 'react';
import ReactDOM from 'react-dom/client';
import Table from './components/Table';

ReactDOM.createRoot(
  document.getElementById('root'))
    .render(
      <StrictMode>
        <Table />
      </StrictMode>
    );
  • Es posible combinar las propiedades por defecto, con la validación de propiedades.

  • ./src/components/Table/index.jsx

import PropTypes from 'prop-types';
import TableBody from "./TableBody";
import TableHead from "./TableHead";

const Table = ({ title }) =>
  <>
    <h1>{ title }</h1>
    <table>
      <TableHead />
      <TableBody />
    </table>
  </>;

Table.propTypes = {
  title: PropTypes.string.isRequired
}

export default Table;
  • ./src/components/Table/index.jsx

import PropTypes from 'prop-types';
import TableBody from "./TableBody";
import TableHead from "./TableHead";

const Table = ({ title }) =>
  <>
    <h1>{ title }</h1>
    <table>
      <TableHead />
      <TableBody />
    </table>
  </>;

Table.defaultProps = {
  title: 'Default Title'
}

Table.propTypes = {
  title: PropTypes.string.isRequired
}

export default Table;

Perforación

  • Se introduce el componente App, como componente raíz de toda la aplicación para favorecer la legibilidad

    • Es el encargado de obtener las filas y columnas para pintar la tabla, propaga las mismas hacia el componente hijo Table, a su vez éste las vuelve a propagar hacia sus hijos TableHead y TableBody, que de nuevo las vuelven a propagar a sus hijos TableColumms y TableRow

diagramaPerforacion1
import Table from './Table';
import TableBody from './Table/TableBody';
import TableColumns from './Table/TableColumns';
import TableHead from './Table/TableHead';

const App = () => {
  const columns = [ 'Id', 'Nombre' ];
  const rows = [
    {"id": 1, "name": "España"},
    {"id": 2, "name": "Andorra"},
    {"id": 3, "name": "Portugal"}
  ];

  return <Table title="React Course" columns={ columns } rows={ rows } />
}

export default App;
import TableBody from "./TableBody";
import TableHead from "./TableHead";

const Table = ({ title, rows, columns }) => <>
  <h1>{ title }</h1>
  <table>
    <TableHead columns={ columns } />
    <TableBody rows={ rows } />
  </table>
</>;

export default Table;
import TableColumns from "./TableColumns";

const TableHead = ({ columns }) =>
  <thead>
    <TableColumns columns={ columns } />
  </thead>;

export default TableHead;
const TableColumns = ({ columns }) =>
  <tr>
    { columns.map(column => <th key={ column }>{ column }</th>) }
  </tr>;

export default TableColumns;
import TableRow from "./TableRow";

const TableBody = ({ rows }) =>
  <tbody>
    { rows.map(row => <TableRow key={ row.id } row={ row } />) }
  </tbody>;

export default TableBody;
const TableRow = ({ row }) =>
  <tr>
    <td>{ row.id }</td>
    <td>{ row.name }</td>
  </tr>;

export default TableRow;
  • La propagación de propiedades (drop drilling) a través de padres a hijos puede provocar problemas de acoplamiento si la jerarquía de componentes no esta bien diseñada.

    • A pesar de tener un claro ejemplo de Prop Drilling, no existe un problema de acoplamiento. Los componentes TableHead y TableBody y sus hijos, no van a existir por si mismos, siempre van a necesitar de su padre Table

    • Todo problema de Prop Drilling debe resolverse mediante composición

    • Una forma sencilla de identificar la propagación de propiedades es, cuando existen componentes en la jerarquía con sus respectivas responsabilidades, pero que no interactúan con dichas propiedades y solo las vuelven a propagar a otros hijos

diagramaPerforacion2
import Table from './Table';
import TableBody from './Table/TableBody';
import TableColumns from './Table/TableColumns';
import TableHead from './Table/TableHead';

const App = () => {
  const columns = [ 'Id', 'Nombre' ];
  const rows = [
    {"id": 1, "name": "España"},
    {"id": 2, "name": "Andorra"},
    {"id": 3, "name": "Portugal"}
  ];

  return <Table title="React Course">
          <TableHead>
            <TableColumns columns={ columns } />
          </TableHead>
          <TableBody rows={ rows } />
        </Table>;
}

export default App;
const Table = ({ title, children }) => <>
  <h1>{ title }</h1>
  <table>
    { children }
  </table>
</>;

export default Table;
  • Gracias a la composición mediante la propiedad children, la propagación de las propiedades se realiza hacia los componentes que las necesitan de forma exclusiva

    • La propiedad children existe por defecto en todos los componentes, y es posible utilizarla como si de una propiedad normal se tratara.

    • Ahora Table no está acoplado a las propiedades columns y rows, y es posible eliminar el componente TableHead en caso de no necesitar las columnas

const TableHead = ({ children }) =>
  <thead>
    { children }
  </thead>;

export default TableHead;
const TableColumns = ({ columns }) =>
  <tr>
    { columns.map(column => <th key={ column }>{ column }</th>) }
  </tr>;

export default TableColumns;
import TableRow from "./TableRow";

const TableBody = ({ rows }) =>
  <tbody>
    { rows.map(row => <TableRow key={ row.id } row={ row } />) }
  </tbody>;

export default TableBody;
const TableRow = ({ row }) =>
  <tr>
    <td>{ row.id }</td>
    <td>{ row.name }</td>
  </tr>;

export default TableRow;

Condicionales

  • La representación condicional es un término que describe la capacidad de renderizar diferentes elementos o componentes basados en una condición

    • Este concepto se aplica a menudo en los siguientes escenarios:

  • Renderización de datos externos desde una API

  • Mostrar u ocultar elementos

  • Alternar la funcionalidad de la aplicación

  • Implementación de niveles de permiso

  • Manejar la autenticación y autorización

Sentencia If

  • El renderizado condicional en React funciona de la misma forma que lo hacen las condiciones en JavaScript.

    • Puedes usar el condicional if para crear elementos dinámicamente en base al valor del estado o las propiedades que recibe el componente.

    • El componente App contiene un componente privado, el componente NoContentInTable, el cual devuelve un mensaje indicando que la tabla está vacía.

      • La función checkEmptyTable hará la comprobación dado el valor de la constante rows para saber si debe renderizar el componente Table o el componente NoContentInTable

    • Los if complejos deben estar fuera del return del componente por rendimiento.

    • Para favorecer la reusabilidad, el comportamiento de la función que envuelve al if, puede hacerse en otro componente.

import Table from './Table';
import TableBody from './Table/TableBody';
import TableColumns from './Table/TableColumns';
import TableHead from './Table/TableHead';

const App = () => {
  const columns = ['Id', 'Nombre'];
  const rows = [];

  const checkEmptyTable = () => {
    if (!rows.length) {
      return <NoContentInTable />;
    }
    return <Table>
      <TableHead>
        <TableColumns columns={columns} />
      </TableHead>
      <TableBody rows={rows} />
    </Table>;
  }

  const NoContentInTable = () =>
    <span>La tabla está vacia.</span>;


  return <>
    <h1>React Course</h1>
    {checkEmptyTable()}
  </>;
}

export default App;

Operador Ternario

  • Mediante un operador ternario podemos acometer el mismo resultado simplificando el componente

import Table from './Table/Table';
import TableBody from './Table/TableBody';
import TableColumns from './Table/TableColumns';
import TableHead from './Table/TableHead';

const App = () => {
  const columns = [ 'Id', 'Nombre' ];
  const rows = [];

  const NoContentInTable = () =>
    <span>La tabla está vacia.</span>;

  return <>
    <h1>React Course</h1>
    {
      !rows.length
      ? <NoContentInTable />
      : <Table>
          <TableHead>
            <TableColumns columns={ columns } />
          </TableHead>
          <TableBody rows={ rows } />
        </Table>
    }
  </>;
}

export default App;

Operador Y-lógico

  • Con el operador y-lógico (&&), con su evaluación con cortocircuito, podemos evitar renderizar un componente según la existencia de un valor o propiedad.

    • La constante rows tiene el valor null en el componente App, debido a que condicionalmente null se convierte en false por coerción de tipos, el componente Table no será renderizado.

import Table from './Table/Table';
import TableBody from './Table/TableBody';
import TableColumns from './Table/TableColumns';
import TableHead from './Table/TableHead';

const App = () => {
  const columns = [ 'Id', 'Nombre' ];
  const rows = null;

  return <>
    <h1>React Course</h1>
    {
      rows &&
      <Table>
        <TableHead>
          <TableColumns columns={ columns } />
        </TableHead>
        <TableBody rows={ rows } />
      </Table>
    }
  </>;
}

export default App;

Valor null

  • Otra de las utilidades del uso de condicionales es evitar el ciclo de vida del componente, no será renderizado y su ciclo de vida será detenido

    • Una validación muy común al trabajar con la propiedad children es { children && children } para renderizar los hijos si existen y evitar errores si estos no están presentes

import TableRow from "./TableRow";

const TableBody = ({ rows }) => {
  if(!rows) {
    return null;
  }
  return <tbody>
    {
      rows.map(row =>
        <TableRow key={ row.id } row={ row } />)
    }
  </tbody>;
}

export default TableBody;

Listados

  • Una colección de elementos o componentes, puede ser representada en JSX haciendo uso de la función map de JavaScript.

    • La función de orden superior map retorna el array de componentes de los elementos que forman las filas

import TableRow from "./TableRow";

const TableBody = ({ rows }) =>
  <tbody>
    {
      rows.map(row =>
        <TableRow key={ row.id } row={ row } />)
    }
  </tbody>;

export default TableBody;
  • Todos los elementos y componentes hacen uso de la propiedad key de manera interna, siendo obligatorio especificar dicha propiedad en los elementos y componentes dentro de bucles

    • Se extraen los datos de las filas a un fichero JSON, el cual es importado y es tratado con la función de orden superior reduce para obtener el formato necesario por la tabla.

    • Siempre debe ser única entre renderizados dentro del ámbito del bucle, de lo contrario el rendimiento del renderizado puede verse afectado.

    • La prop key permite controlar las instancias de los componentes cada vez que se renderizan, llamando a las funciones para recuperar los nuevos elementos que utiliza para actualizar el DOM, si devuelve los mismos tipos de elementos identificados por la propiedad key mantiene esos nodos del DOM, aunque todas las props hayan cambiado.

import Table from './Table/Table';
import TableBody from './Table/TableBody';
import TableColumns from './Table/TableColumns';
import TableHead from './Table/TableHead';
import countries from '../countries.json';

const App = () => {
  const columns = ['Id', 'Nombre'];
  const rows = () =>
    countries.reduce((acc, country) => {
      acc.push({
        "id": country.cca2,
        "name": country.name.common
      })
      return acc;
    }, []);

  return <>
    <h1>React Course</h1>
    <Table>
      <TableHead>
        <TableColumns columns={columns} />
      </TableHead>
      <TableBody rows={rows()} />
    </Table>
  </>;
}

export default App;

Componentes de Orden Superior

  • Un componente de orden superior (HOC) es una técnica avanzada para reutilizar la lógica en los componentes

    • Los componentes toman uno o más componentes como argumentos, y devuelven un nuevo componente actualizado.

      • similares a las funciones de orden superior, que toman algunas funciones como argumento y producen una nueva función

    • Por convención de nombres, utilizan el prefijo With en PascalCase

  • Decorator es un patrón de diseño estructural que te permite añadir funcionalidades a objetos colocando estos objetos dentro de objetos encapsuladores especiales que contienen estas funcionalidades.

const WithProtected = Component => props => {
  const { isProtected, ...properties } = props;
  return !isProtected
    ? <Component {...properties} />
    : <span>El componente está protegido...</span>
}

export default WithProtected;
import Table from './Table/Table';
import TableBody from './Table/TableBody';
import TableColumns from './Table/TableColumns';
import TableHead from './Table/TableHead';
import WithProtected from './WithProtected/WithProtected';
import countries from '../countries.json';

const ProtectedTable = WithProtected(Table);

const App = () => {
  const columns = [ 'Id', 'Nombre' ];
  const rows = () => countries.reduce((acc, country) => {
    acc.push({
      "id": country.cca2,
      "name": country.name.common
    })
    return acc;
  }, []);

  return <>
    <h1>React Course</h1>
    <ProtectedTable isProtected={ false }>
      <TableHead>
        <TableColumns columns={ columns } />
      </TableHead>
      <TableBody rows={ rows() } />
    </ProtectedTable>
  </>;
}

export default App;

CSS

Con biblioteca sass!!!

CSS Global

CSS en Línea

Módulos CSS

CSS en Javascript

CSS Global

  • La forma tradicional es tener archivos scss que contengan las clases y estilos de los componentes que construyen la aplicación.

    • Se debe importar el archivo scss

    • Usualmente se utiliza el paquete classnames para poder condicionar cuando se aplica una clase según el comportamiento del componente

  • ./src/components/Table/Table.jsx

const Table = ({ children }) =>
  <table className="my-table">
    { children }
  </table>;

export default Table;
  • ./src/styles.scss

.my-table {
    background-color: #b8b8b8;
}
  • ./src/components/App.jsx

import Table from './Table/Table';
import TableBody from './Table/TableBody';
import TableColumns from './Table/TableColumns';
import TableHead from './Table/TableHead';
import countries from '../countries.json';
import '../styles.scss';

const App = () => {
  const columns = [ 'Id', 'Nombre' ];
  const rows = () =>
    countries.reduce((acc, country) => {
      if(country.population >= 100000) {
        acc.push({
          "id": country.cca2,
          "name": country.name.common
        });
      }
      return acc;
    }, []);

  return <>
    <h1>React Course</h1>
    <Table>
      <TableHead>
        <TableColumns columns={ columns } />
      </TableHead>
      <TableBody rows={ rows() } />
    </Table>
  </>;
}

export default App;

CSS en Línea

  • Mediante la propiedad style de JSX para contener los estilos en cada uno de los componentes

    • El CSS se define ahora de una manera ligeramente diferente

    • El valor que recibe la propiedad style va entre las llaves dobles:

      • las primeras indican que se va a utilizar JavaScript

      • las segundas indican el objeto que representan los estilos CSS

    • Las propiedades CSS se definen dentro del objeto en formato camelCase

  • ./src/components/Table/Table.jsx

const Table = ({ children }) =>
  <table style={
    {
      backgroundColor: '#b8b8b8'
    }
  }>
    { children }
  </table>;

export default Table;

Módulos CSS

  • Un módulo CSS es un archivo CSS en el que todos los nombres de clase y de animación tienen un ámbito local por defecto.

  • Los módulos CSS permiten escribir estilos en archivos CSS pero consumirlos como objetos JavaScript para un procesamiento y seguridad adicionales. Los módulos CSS son muy populares porque hacen que los nombres de las clases y las animaciones sean automáticamente únicos, por lo que no hay que preocuparse por las colisiones de nombres de los selectores.

    • Por convención el nombre de los ficheros CSS se forma con el nombre del componente seguido de module

    • se recomienda utilizar el nombre de las clases en formato camelCase, al tratar el CSS como un objeto de JavaScript

  • ./src/components/Table/Table.jsx

import styles from "./Table.module.scss"

const Table = ({ children }) =>
  <table className={ styles.myTable }>
    { children }
  </table>;

export default Table;
  • ./src/components/Table/Table..module.scss

.myTable {
    background-color: #b8b8b8;
}

CSS en Javascript

Con biblioteca styled-components!!!

  • Permite abstraer el CSS al nivel del propio componente, en lugar de al nivel del documento, utilizando JavaScript para describir los estilos de forma declarativa y manteníble.

    • Cuando este JavaScript se analiza, se genera CSS.

    • Es capaz de otras cosas que no eran posibles utilizando las técnicas tradicionales de CSS

      • estilos dinámicos en línea con sólo unas pocas declaraciones condicionales

      • código más modular con su CSS encapsulado en el mismo bloque que el JavaScript del usuario, delimitándolo sólo a ese módulo.

  • ./src/components/Table/Table.jsx

import styled from 'styled-components';

const MyStyledTable = styled.table`
  background-color: #b8b8b8;
`;

const Table = ({ children }) =>
  <MyStyledTable>
    { children }
  </MyStyledTable>;

export default Table;
Beneficios:
  • Modularidad pensando en componentes, no es necesario que los usuarios tengan que mantener un montón de hojas de estilo

  • toda la potencia del ecosistema JavaScript para mejorar el CSS

  • Verdadero aislamiento de reglas. Los selectores de ámbito no son suficientes. CSS tiene propiedades que se heredan automáticamente del elemento padre, si no se definen explícitamente.

  • Prefijación de proveedores. Las reglas CSS están prefijadas automáticamente por el proveedor, por lo que los desarrolladores no tienen que pensar en ello

  • Selectores de ámbito. CSS sólo tiene un espacio de nombres global. Es imposible evitar colisiones de selectores en aplicaciones no triviales. CSS en JavaScript genera nombres de clase únicos por defecto cuando se compila a CSS.

  • Compartir código fácilmente, constantes y funciones entre JavaScript y CSS

  • Sólo los estilos que están actualmente en uso en las pantallas de los usuarios están en el DOM

  • Eliminación de código muerto

  • Si el componente ya esta creado y no es posible crearlo con styled-component, podemos envolver el componente utilizando la función styled y aplicar los estilos.

    • El componente envuelto recibe por parámetro la clase con los estilos, esta debe usarse en la propiedad className del componente envuelto.

  • ./src/components/Table/Table.jsx

import styled from 'styled-components';

const Table = ({ className, children }) =>
  <table className={ className }>
    { children }
  </table>;

const MyStyledTable = styled(Table)`
  background-color: #b8b8b8;
`;

export default MyStyledTable;

Renderizado

  • desacoplar del componente

    • useContext

    • useReducer

  • Ciclo de Vida

    • useState

    • useEffecct

    • customHook

    • otros tipos hook: pendiente!!!

  • Estado Global

    • userRef

    • acceder a Real DOM

    • gestion de claves para renderizar optimizado

    • useMemo recordar valores y no renderizar

    • useCallback recordar función

Portales

  • Los portales proporcionan una opción de primera clase para renderizar hijos en un nodo DOM que existe por fuera de la jerarquía del DOM del componente padre

import ReactDOM from 'react-dom'

const Modal = ({ children }) => {
        return ReactDOM.createPortal(
    children,
    document.querySelector('#modal-root')
  );
}

const Components = () => {
        return <Modal />
}

Límites de Errores

  • Los límites de errores son componentes de React que capturan errores de JavaScript en cualquier parte de su árbol de componentes hijo, registran esos errores, y muestran una interfaz de repuesto en lugar del árbol de componentes que ha fallado. Los límites de errores capturan errores durante el renderizado, en métodos del ciclo de vida, y en constructores de todo el árbol bajo ellos.

Librerías

Pruebas

Bibliografía

Obra, Autor y Edición Portada Obra, Autor y Edición Portada

Ponente

  • Christian Bohollo Sáez

  • Grado Superior Desarrollo Aplicaciones

  • Grado Superior Administración de Sistema

  • Oma Technolgies S.L.

  • Luis Fernández Muñoz

setillo

  • Doctor en Inteligencia Artificial por la UPM

  • Ingeniero en Informática por la UMA

  • Diplomado en Informática por la UPM

  • Profesor Titular de ETSISI de la UPM