Skip to content

Instantly share code, notes, and snippets.

@khriztianmoreno
Last active July 25, 2022 15:38
Show Gist options
  • Save khriztianmoreno/a2817aca5138e6f6461bb849dc126b64 to your computer and use it in GitHub Desktop.
Save khriztianmoreno/a2817aca5138e6f6461bb849dc126b64 to your computer and use it in GitHub Desktop.
Blog Posts

Destructuring es quizá una de las mas grandes características agregadas a ES6 (2015) y ampliamente utilizada hoy en día, por lo que comprender que es y poder leerlo fácilmente en el código se convierte en algo necesario en tu día a día.

¿Qué significa destructuring?

Es una expresión que te permite extraer o “destructurar” datos desde estructuras de datos como arreglos, objetos, mapas y sets y crear nuevas variables con ese dato en particular.

Te permite extraer propiedades de un objeto, items de un arreglo de una manera “sencilla” y de una sola vez.

Pensemos en una estructura de datos común y una tarea repetitiva

const usuario = {
    firstName: 'Cristian',
    lastName: 'Moreno',
    country: 'Colombia',
    twitter: '@khriztianmoreno'
}
const firstName = usuario.firstName
const lastName = usuario.lastName

Es una tarea común, obtener alguna propiedad de un objeto para ser utilizada dentro de otro bloque lógico, puedes asignarla a una variable como en este caso, o escribir usuario.firstName varias veces más adelante.

Lo que destructuring permite, es hacer este proceso de una manera más directa:

const { firstName, lastName } = usuario

Esta es la sintaxis de destructuración de un objeto, usamos {} al lado izquierdo de una asignación para indicar que estamos des-tructurando el valor que está al lado derecho.

Este código simplemente dice: Crea dos variables: firstName y lastName y toma desde el objeto usuario las propiedades de igual nombre y asigna sus valores.

Genial no?. Ese es sólo un nivel, esto puede ser utilizando de forma anidada tantas veces como desees.

const usuario = {
  firstName: 'Cristian',
  lastName: 'Moreno',
  links: {
    social: {
      twitter: '@khriztianmoreno'
    }
  } 
}
const { twitter } = usuario.links.social
// o tambien se puede
const { links: { social: { twitter } } } = usuario

Puedes destructurar en tanto niveles como gustes, pero intenta siempre mantener la legibilidad.

Pero esta sintaxis no está limitada solo a objetos, también podemos destructurar arreglos.

const arreglo = ['Hola','soy','Khriztianmoreno']

Digamos que este arreglo es igual en muchas ocasiones y que queremos extraer el saludo y el nombre. ¿Cómo lo harías?

La forma sin destructuración sería

const saludo = arreglo[0]
const nombre = arreglo[2]

Pero si usamos destruturación podemos nombre las variables de forma directa

const [saludo, , nombre ] = arreglo

¿Viste lo que hice ahí?

Al destructurar un arreglo hacemos uso de la posición del elemento en el arreglo para las asignaciones, así saludo es equivalente a arreglo[0]. Esto también permite “saltarse” algún item del arreglo que no nos interesa, simplemente escribimos un “fantasma”. En vez de escribir el nombre de la variable que no nos interesa, en este caso la posición central o arreglo[1] simplemente escribimos las ,(coma) correspondiente.

Veamos un ejemplo más “interesante”

function doSomeCalc({x, y, z = 10}( {
  return Math.floor((x + y + z ) / 3)
}
// que es lo mismo que
function doSomCalc(obj) {
  const { x, y, z = 10 } = obj
  return Math.floor((x + y + z ) / 3)
}

En MDN puedes conocer más información.

Una vez revises el contenido del artículo, te propongo el siguiente desafío: refactorizar el siguiente código utilizando desctructuring.

function doSomeDestructuring(){
  const data = {
    title: 'Wanda Vision',
    protagonistas: [
      {
        name: 'Wanda Maximof',
        enemies: [
          {name: 'Grim Reaper'},
          {name: 'Nightmare' },
          {name: 'Agatha Harkness'},
          {name: 'Zelena', title: 'The Wicked Witch'},
        ],
      },
      {
        name: 'Vision',
        enemies: [
          { name: 'Doctor Doom' },
          { name: 'Ultron' }
        ]
      }
    ],
  }
  // const {} = data // <-- Reemplaza el uso de múltiples const con destructuring
  const title = data.title
  const protagonistName = data.protagonists[0].name
  const enemy = data.protagonists[0].enemies[3]
  const enemyName = enemy.name
  return `${title}: La protagonista ${protagnistName} lucha contra ${enemyName}`
}

Introducción de 5 minutos a las API REST

Hoy aprenderemos los conceptos básicos de las API y qué hace que una API sea "RESTful". Las API son útiles para permitir la comunicación entre sistemas de software, como podría ocurrir entre una aplicación móvil y un servidor. Si desea comprender cómo funcionan estos sistemas, aprender sobre las API es esencial.

Entonces, ¿qué es una API?

Una API (interfaz de programación de aplicaciones) es un conjunto de reglas que permite que una pieza de software se comunique con otra. Las API también nos facilitan la vida al abstraer el código complejo y reemplazarlo por un código más simple y directo.

Por ejemplo, piense en lo que sucede cuando abre una aplicación de transmisión de música y reproduce una canción.

IMAGEN SPOTIFY PLAY

Probablemente hay algún código que se ejecuta cuando presionas reproducir que se parece a esto:

mediaPlayer.play();

Esa función play() es un método API que abstrae cosas como:

  • El código necesario para recuperar el archivo de audio de Internet o de su dispositivo.
  • El código necesario para enviar datos de audio a su dispositivo.

Como desarrolladores, no necesitamos saber o preocuparnos por el código de nivel inferior cuando llamamos al método play (). Las API están aquí para hacernos la vida más fácil.

Cómo funcionan las API

En JavaScript, las API generalmente se basan en objetos. En el ejemplo anterior, el objeto mediaPlayer es el punto de entrada para la API y play() es el método de la API.

Echemos un vistazo a un ejemplo más común que probablemente haya encontrado antes. Si alguna vez ha escrito JavaScript como este ...

Screen-Recording-2021-08-19-at-1

Introducción a Volta, la forma más rápida de administrar entornos de Node

Volta, es una herramienta que abre las posibilidades para una experiencia de desarrollo más fluida con Node.js. Esto es especialmente relevante para trabajar en equipo. Volta te permite automatizar tu entorno de desarrollo Node.js. Permite a tu equipo usar las mismas versiones consistentes de Node y otras dependencias. Aún mejor, permite mantener las versiones consistentes en los entornos de producción y desarrollo, eliminando los errores sutiles que vienen con los desajustes de versiones.

Volta elimina los problemas de "Funciona en mi máquina ..."

Los desajustes de versiones causan dolores de cabeza cuando se desarrolla en equipo.

Supongamos este escenario:

El equipo X creó su aplicación en máquinas locales que ejecutaban Node 10, pero la canalización de compilación adoptó de forma predeterminada la versión de Node más baja que tenía a mano, Node 6, y la aplicación no se iniciaba en producción. Tuvieron que revertir el despliegue, averiguar qué salió mal, se convirtió en una noche muy larga.

Si hubieran usado Volta esto pudo haberse evitado.

Como trabaja Volta?

Volta es "una forma sencilla de administrar sus herramientas de línea de comandos de JavaScript". Hace que la administración de Node, npm, yarn u otros ejecutables de JavaScript enviados como parte de los paquetes, sea realmente fácil.

Volta tiene mucho en común con herramientas como NVM, pero NVM no es la más fácil de configurar inicialmente y, lo que es más importante, el desarrollador que lo usa todavía tiene que recordar cambiar a la versión correcta de Node para el proyecto en el que están trabajando.

Volta, por otro lado, es fácil de instalar y elimina la parte pensante de la ecuación: una vez que Volta esté configurado en un proyecto e instalado en una máquina local, cambiará automáticamente a las versiones adecuadas de Node.

No solo eso, sino que también le permitirá definir versiones de yarn y npm en un proyecto, y si la versión de Node definida en un proyecto no se descarga localmente, Volta saldrá y descargará la versión apropiada.

Pero cuando cambia a otro proyecto, Volta cederá a los ajustes preestablecidos en ese proyecto o volverá a las variables de entorno predeterminadas.

Volta en acción

Demos a Volta a dar una vuelta. Primero, cree una nueva aplicación React con Create React App.

Ejecute el siguiente comando desde una terminal.

npx create-react-app volta-sample-app

Una vez que haya creado su nueva aplicación React, abra el código en un IDE e inícielo a través de la línea de comando.

npm run start

Si todo va según el plan, verá un logotipo de React giratorio cuando abra un navegador en http://localhost:3000/

Ahora que tenemos una aplicación, agreguemos Volta.

Descarga Volta localmente

Para instalar Volta, ejecute el siguiente comando:

curl https://get.volta.sh | bash

Si tiene Windows, descargue y ejecute el instalador de Windows y siga las instrucciones.

Defina sus variables de entorno

Antes de agregar nuestras versiones de Node y npm específicas de Volta a nuestro proyecto, veamos cuáles son las variables de entorno predeterminadas.

Obtenga una lectura de referencia

En una terminal en la raíz de su proyecto, ejecute el siguiente comando.

node -v && npm -v

Para mí, mis versiones predeterminadas de Node y npm son v14.18.1 y v6.14.15, respectivamente.

Con nuestra línea de base establecida, podemos cambiar nuestras versiones solo para este proyecto con la ayuda de Volta.

Fijar una version de node.js

Empezaremos con Node. Dado que v16 es la versión actual de Node, agreguemos eso a nuestro proyecto.

En nuestro proyecto en el nivel raíz donde vive nuestro archivo package.json, ejecute el siguiente comando.

volta pin node@16

El uso de volta pin [JS_TOOL]@[VERSION] pondrá esta herramienta JavaScript en particular en nuestra versión especificada en el package.json de nuestra aplicación. Después de enviar esto a nuestro repositorio con git, cualquier desarrollador futuro que use Volta para administrar dependencias podrá leer esto del repositorio y usar exactamente la misma versión.

Con Volta podemos ser tan específicos o genéricos como deseamos definir las versiones, y Volta llenará cualquier vacío. Especifiqué la versión principal de Node que quería (16) y luego Volta completó las versiones menores y de parche por mí.

Después de anclar, verá el siguiente mensaje de éxito en su terminal: pinned node@16.11.1 in package.json.

Consejo: haga que su versión de node anclada coincida con la versión de Node de su servidor de compilación

Fijar una version de npm

Ahora abordemos nuestra versión npm. Aún en la raíz de nuestro proyecto en la terminal, ejecute este comando:

volta pin npm

Sin una versión especificada, Volta elige por defecto la última versión de LTS para agregar a nuestro proyecto.

La versión actual de LTS para npm es 8, por lo que ahora nuestro proyecto tiene npm v8.1.0 como su versión predeterminada.

Verificar el package.json

Para confirmar que las nuevas versiones del entorno de JavaScript son parte de nuestro proyecto, verifique el archivo package.json de la aplicación.

Desplácese hacia abajo y debería ver una nueva propiedad llamada "volta". Dentro de la propiedad "volta" debe haber un "node": "16.11.1" y una versión "npm": "8.1.0"

A partir de ahora, cualquier desarrollador que tenga Volta instalado en su máquina y baje este repositorio tendrá la configuración de estas herramientas para cambiar automáticamente para usar estas versiones particulares de node y npm.

Para estar doblemente seguro, también puede volver a ejecutar el primer comando que hicimos antes de anclar nuestras versiones con Volta para ver cómo está configurado nuestro entorno de desarrollo actual.

node -v && npm -v

Después de esto, su terminal debería decirle que está usando esas mismas versiones: Node.js v16 y npm v8.

Mira cómo sucede la magia

Ahora, puedes sentarte y dejar que Volta se encargue de las cosas por ti.

Si deseas ver qué sucede cuando no hay nada especificado para Volta, intente subir un nivel desde la raíz de su proyecto y verifique sus versiones de Node y npm nuevamente.

Abramos dos terminales una al lado de la otra: la primera dentro de nuestro proyecto con versiones de Volta, la otra un nivel más alto en nuestra estructura de carpetas.

Ahora ejecute el siguiente comando en ambos:

node -v && npm -v

Y en nuestro proyecto, Node v16 y npm v8 se están ejecutando, pero fuera del proyecto, Node v14 y npm v6 están presentes. No hicimos nada más que cambiar de directorio y Volta se encargó del resto.

Al usar Volta, eliminamos las conjeturas de nuestras variables de entorno de JavaScript y, de hecho, dificultamos que un miembro del equipo de desarrollo use las versiones incorrectas que las correctas.

Un manual práctico sobre los sistemas de módulos de JavaScript

Hoy te daré una introducción práctica sobre los sistemas de módulos que usamos cuando construimos bibliotecas en JavaScript. A medida que una aplicación web o biblioteca crece y se agregan más funciones, la modularización del código mejora la legibilidad y el mantenimiento. Esta guía rápida le dará una visión incisiva de las opciones disponibles para crear y consumir módulos en JavaScript.

Si alguna vez se ha preguntado cuáles son los pros y los contras de AMD, ESM o CommonJS, esta guía le brindará la información que necesita para elegir con confianza entre todas las opciones.

Una historia de módulos de JavaScript

Sin funciones nativas integradas para espacios de nombres y módulos en las primeras versiones del lenguaje JavaScript, se han introducido diferentes formatos de módulo a lo largo de los años para llenar este vacío.

Los más notables, que le mostraré cómo usar en su código JavaScript a continuación, son:

  • Expresión de función inmediatamente invocada (IIFE)
  • CommonJS (CJS)
  • Definición de módulo asíncrono (AMD)
  • Definición de módulo universal (UMD)
  • Módulos ECMAScript (ESM)

La selección de un sistema de módulos es importante al desarrollar una biblioteca de JavaScript. Para los autores de bibliotecas, la elección del sistema de módulos para su biblioteca afecta la adopción por parte del usuario y la facilidad de uso. Querrá estar familiarizado con todas las posibilidades.

1. Expresión de función inmediatamente invocada (IIFE) - Immediately Invoked Function Expression

Una de las primeras formas de exponer bibliotecas en el navegador web, las expresiones de función invocadas inmediatamente (IIFE) son funciones anónimas que se ejecutan inmediatamente después de ser definidas.

(function () {
  // Module's Implementation Code
})();

Un patrón de diseño común que aprovecha los IIFE es el patrón Singleton, que crea una única instancia de objeto y código de espacios de nombres. Este objeto sirve como un único punto de acceso a un grupo específico de funciones. Para ver ejemplos del mundo real, no busque más allá del objeto Math o la biblioteca jQuery.

Pros

Escribir módulos de esta manera es conveniente y compatible con navegadores más antiguos. De hecho, puede concatenar y agrupar de forma segura varios archivos que contienen IIFE sin preocuparse por las colisiones de nomenclatura y alcance.

Contras

Sin embargo, los módulos IIFE se cargan sincrónicamente, lo que significa que ordenar correctamente los archivos del módulo es fundamental. De lo contrario, la aplicación se romperá. Para proyectos grandes, los módulos IIFE pueden ser difíciles de administrar, especialmente si tiene muchas dependencias superpuestas y anidadas.

2. CommonJS (CJS)

El sistema de módulos predeterminado de Node.js, CommonJS (CJS) usa la sintaxis require para importar módulos y la sintaxismodule.exports y export para exportaciones predeterminadas y con nombre, respectivamente. Cada archivo representa un módulo y todas las variables locales del módulo son privadas, ya que Node.js envuelve el módulo dentro de un contenedor de funciones.

Por ejemplo, este módulo ..

const { PI, pow } = Math;

function calculateArea(radius) {
  return PI * pow(radius, 2);
}

module.exports = calculateArea;

Se convierte en ...

(function (
  exports,
  require,
  module,
  __filename,
  __dirname
) {
  const { PI, pow } = Math;

  function calculateArea(radius) {
    return PI * pow(radius, 2);
  }

  module.exports = calculateArea;
});

El módulo no solo tiene sus variables dentro del ámbito privado, sino que aún tiene acceso global a, exports, require y module. __filename y __dirname tienen el ámbito del módulo y contienen el nombre del archivo y el nombre del directorio del módulo, respectivamente.

La sintaxis requerida le permite importar módulos incorporados de Node.js o módulos de terceros instalados localmente

Pros

Las sentencias require de CommonJS son síncronas, lo que significa que los módulos de CommonJS se cargan de forma síncrona. Siempre que sea el único punto de entrada de la aplicación, CommonJS automáticamente sabe cómo ordenar los módulos y manejar las dependencias circulares.

Contras

Al igual que los IIFE, CommonJS no se diseñó para generar paquetes de tamaño pequeño. El tamaño del paquete no se tuvo en cuenta en el diseño de CommonJS, ya que CommonJS se utiliza principalmente para desarrollar aplicaciones del lado del servidor. Para las aplicaciones del lado del cliente, el código debe descargarse primero antes de ejecutarlo. La falta de agitación de árboles convierte a CommonJS en un sistema de módulos no óptimo para aplicaciones del lado del cliente.

3. Definición de módulo asíncrono (AMD) - Asynchronous Module Definition

A diferencia de IIFE y CommonJS, Asynchronous Module Definition (AMD) carga módulos y sus dependencias de forma asincrónica. Con origen en Dojo Toolkit, AMD está diseñado para aplicaciones del lado del cliente y no requiere herramientas adicionales. De hecho, todo lo que necesita para ejecutar aplicaciones siguiendo el formato del módulo AMD es la biblioteca RequireJS, un cargador de módulos en el navegador. Eso es. Aquí hay un ejemplo simple que ejecuta una aplicación React simple, estructurada con AMD, en el navegador.

<!-- index.html -->
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      http-equiv="X-UA-Compatible"
      content="IE=edge"
    />
    <meta
      name="viewport"
      content="width=device-width, 
      initial-scale=1.0"
    />
    <title>React + AMD</title>
  </head>
  <body>
    <div id="root"></div>
    <script
      type="text/javascript"
      src="https://cdnjs.cloudflare.com
       /ajax/libs/require.js/2.3.6
       /require.min.js"
    ></script>
    <script
      type="text/javascript"
      src="main.js"
    ></script>
  </body>
</html>

A continuación, este es el aspecto de JavaScript.

// main.js
requirejs.config({
  paths: {
    react:
      "https://unpkg.com/react@15.3.2
      /dist/react",
    "react-dom":
      "https://unpkg.com
      /react-dom@15.3.2
      /dist/react-dom",
  },
});

requirejs(
  ["react", "react-dom"],
  (React, ReactDOM) => {
    ReactDOM.render(
      React.createElement(
        "p",
        {},
        "Greetings!"
      ),
      document.getElementById("root")
    );
  }
);

Llamar a los métodos requirejs o define registra la función de fábrica (la función anónima pasada como segundo argumento a estos métodos). AMD ejecuta esta función solo después de que se hayan cargado y ejecutado todas las dependencias.

Pros

AMD permite definir varios módulos dentro de un solo archivo y es compatible con navegadores más antiguos.

Contras

AMD no es tan popular como los formatos de módulos más modernos, como los módulos ECMAScript y la definición de módulo universal.

4. Definición de módulo universal (UMD) - Universal Module Definition

Para las bibliotecas que admiten entornos del lado del cliente y del lado del servidor, la Definición de módulo universal (UMD) ofrece una solución unificada para hacer que los módulos sean compatibles con muchos formatos de módulo diferentes, como CommonJS y AMD.

Aquí está UMD en acción desde la biblioteca de desarrollo de React

(function (root, factory) {
  if (
    typeof define === "function" &&
    define.amd
  ) {
    // Checks for RequireJS's
    // `define` function.
    // Register as an anonymous module.
    define(["exports"], factory);
  } else if (
    typeof exports === "object" &&
    typeof exports.nodeName !== "string"
  ) {
    // Checks for CommonJS.
    // Calls the module factory
    // immediately.
    factory(exports);
  } else {
    // Register browser globals.
    global = root || self;
    factory((global.React = {}));
  }
})(this, function (exports) {
  "use strict";

  // Place React's module code here.
  // ...
});
  • Si el IIFE detecta una función de definición en el alcance global y una propiedad amd en la definición, entonces ejecuta el módulo como un módulo AMD.
  • Si el IIFE detecta un objeto de exportación en el ámbito global y una propiedad nodeName dentro de las exportaciones, entonces ejecuta el módulo como un módulo CommonJS.

Pros

Independientemente de si una aplicación consume su biblioteca como un módulo CommonJS, AMD o IIFE, UMD verifica condicionalmente el formato del módulo que se está utilizando en tiempo de ejecución y ejecuta código específico para el formato del módulo detectado.

Contras

El código de la plantilla UMD es un IIFE de aspecto intimidante y su uso es inicialmente un desafío. Sin embargo, UMD en sí no es conceptualmente complicado.

5. Módulos ECMAScript (ESM)

Los módulos ECMAScript (ESM), el formato de módulo introducido más recientemente, es la forma estándar y oficial de manejar módulos en JavaScript. Este formato de módulo se usa comúnmente en aplicaciones TypeScript.

Al igual que CommonJS, ESM proporciona varias formas de exportar código: exportaciones predeterminadas o exportaciones con nombre.

// circle.js
export function calculateArea() {
  return Math.PI * Math.pow(radius, 2);
}

export function 
 calculateCircumference() {
  return 2 * Math.PI * radius;
}

La importación de estas exportaciones nombradas por separado le dice al paquete de módulos qué partes del módulo importado deben incluirse en el código generado. Se omiten todas las exportaciones con nombre no importadas. Esto reduce el tamaño de la biblioteca, lo cual es útil si su biblioteca se basa en algunos métodos de una biblioteca de utilidades grande como lodash.

Ahora, en algún archivo en el mismo directorio que ./circle.js, necesitaríamos el módulo de la siguiente manera.

const {
  calculateArea,
  calculateCircumference,
} = require("./circle");

console.log(calculateArea(5));
console.log(calculateCircumference(5));

Pros

Los paquetes de módulos son compatibles con ESM y optimizan el código mediante técnicas como la agitación de árboles (elimina el código no utilizado del resultado final), que no son compatibles con otros formatos de módulo. La carga y análisis de módulos es asincrónica, pero su ejecución es sincrónica.

Contras

Este es el sistema de módulos principales más nuevo. Como tal, algunas bibliotecas aún no lo han adoptado.

Creación de tu propia biblioteca de React/JavaScript

Como puede imaginar, elegir el sistema de módulos correcto se vuelve importante al crear tu propia biblioteca React. Personalmente con el uso hoy en dia de herramientas como babel.js podriamos trabajar con modulos de ECMAScript, pero yo soy partidario de usar CommonJS en Node y ECMAScript Modules (ESM) en el frontend.

NPM dependencies vs devDependencies

tl;dr

Las dependencies son requeridas por nuestra aplicación en tiempo de ejecución. Paquetes como react, redux y lodash son todos ejemplos de dependencias. Las devDependencies solo son necesarias para desarrollar o compilar su aplicación. Paquetes como babel, enzyme y prettier son ejemplos de devDependencies.

npm install

La diferencia real entre dependencies y devDependencies se ve cuando ejecuta npm install.

Si ejecuta npm install desde un directorio que contiene un archivo package.json (lo que normalmente hace después de clonar un proyecto, por ejemplo).

✅ Se instalarán todos los paquetes ubicados en dependencies ✅ Se instalarán todos los paquetes ubicados en devDependencies

Si ejecuta npm install <package-name> (lo que normalmente hace cuando desea agregar un nuevo paquete aL proyecto existente), es decir, npm install react.

✅ Se instalarán todos los paquetes ubicados en dependencies ❌ No se instalará ninguno de los paquetes ubicados en devDependencies

Dependencias transitivas

Si el paquete A depende del paquete B y el paquete B depende de C, entonces el paquete C es una dependencia transitiva del paquete A. Lo que eso significa es que para que el paquete A se ejecute correctamente, necesita el paquete B instalado. Sin embargo, para que el paquete B se ejecute correctamente, es necesario que esté instalado el paquete C. ¿Por qué menciono esto? Bueno, las dependencies y devDependencies también tratan las dependencias transitivas de manera diferente.

Cuando ejecutas npm install desde un directorio que contiene un archivo package.json:

  • dependencies ✅ Descarga todas las dependencias transitivas.
  • devDependencies ❌ No descarga ninguna dependencia transitiva.

Especificar dependencies frente a devDependencies

A partir de NPM 5, cuando ejecuta npm install <package-name>, ese paquete se guardará automáticamente dentro de sus dependencies en su archivo package.json. Si quisiera especificar que el paquete específico debería incluirse en devDependencies en su lugar, agregaría la marca --save-dev.

npm install prettier --save-dev

Instalación en un servidor de producción

A menudo, necesitará instalar su proyecto en un servidor de producción. Cuando haga eso, no querrá instalar devDependencies ya que obviamente no las necesitará en su servidor de producción. Para instalar solo las dependencies (y no devDependencies), puede usar la marca --production.

npm install --production

Una mejor manera de crear bibliotecas de componentes React

Hoy repasarémos rápidamente cuatro patrones de programación que se aplican a los componentes compartidos en React.

El uso de estos permite crear una biblioteca de componentes compartidos bien estructurada. El beneficio que obtiene es que los desarrolladores de tu organización pueden reutilizar fácilmente los componentes en numerosos proyectos. Tú y tu equipo serán más eficientes.

Patrones comunes

En este post, te muestro cuatro patrones de API que puede usar con todos sus componentes compartidos. Estos son:

  • JSX children pass-through
  • React fowardRef API
  • JSX prop-spreading cont TypeScript
  • Opinionated prop defaults

Patrón 1: JSX Children Pass-Through

React ofrece la posibilidad de componer elementos utilizando la prop children. El diseño de componentes compartidos se apoya en gran medida en este concepto.

Permitir que los consumidores proporcionen el children siempre que sea posible les facilita proporcionar contenido personalizado y otros componentes. También ayuda a alinear las API de componentes con las de los elementos nativos.

Supongamos que tenemos un componente Button para empezar. Ahora permitimos que nuestro componente Button represente sus children, de la siguiente manera:

// File: src/Button.tsx

export const Button: React.FC = ({ children }) => {
  return <button>{children}</button>;
};

La definición de React.FC ya incluye a children como una prop válida. Lo pasamos directamente al elemento de botón nativo.

A continuación, un ejemplo con Storybook para proporcionar contenido al Button.

// File: src/stories/Button.stories.tsx

const Template: Story = (args) => (
  <Button {...args}>
    my button component
  </Button>
);

Patrón 2: forwardRef API

Muchos componentes tienen una asignación de uno a uno a un elemento HTML. Para permitir que los consumidores accedan a ese elemento subyacente, proporcionamos una prop de referencia utilizando la API React.forwardRef().

No es necesario proporcionar un red para el desarrollo diario de React, pero es útil dentro de las bibliotecas de componentes compartidos. Permite una funcionalidad avanzada, como colocar una información sobre herramientas en relación con nuestro Button con una biblioteca de posicionamiento.

Nuestro componente Button proporciona un solo HTMLButtonElement (button). Le proporcionamos una referencia con forwardRef().

// File: src/buttons/Button.tsx

export const Button =
  React.forwardRef<HTMLButtonElement>(
    ({ children }, ref) => {
      return (
        <button ref={ref}>
          {children}
        </button>
      );
    }
  );

Button.displayName = "Button";

Para ayudar a los consumidores de TypeScript a entender qué elemento se devuelve desde el objeto ref, proporcionamos una variable type que representa el elemento al que lo estamos pasando, HTMLButtonElement en este caso.

Patrón 3: JSX Prop-Spreading

Otro patrón que aumenta la flexibilidad de los componentes es la propagación de props. La propagación de props permite a los consumidores tratar nuestros componentes compartidos como reemplazos directos para sus contrapartes nativas durante el desarrollo.

La propagación de props ayuda con los siguientes escenarios:

  1. Proporcionar props accesibles para cierto contenido.
  2. Agrega atributos de datos personalizados para pruebas automatizadas
  3. Utiliza un evento nativo que no esta definido en nuestras props.

Sin propagación de props, cada uno de los escenarios anteriores requeriría que se definieran atributos explícitos. La propagación de props ayuda a garantizar que nuestros componentes compartidos permanezcan tan flexibles como los elementos nativos que utilizan internamente.

Agreguemos propagación de props a nuestro componente Botón.

// File: src/buttons/Button.tsx

export const Button = React.forwardRef<
  HTMLButtonElement,
  React
   .ComponentPropsWithoutRef<'button'>
>(({ children, ...props }, ref) => {
  return (
    <button ref={ref} {...props}>
      {children}
    </button>
  );
});

Podemos hacer referencia a nuestras props restantes con la sintaxis de propagación y aplicarlos al botón. React.ComponentPropsWithoutRef es una utilidad de tipos que ayuda a documentar los props válidos para un elemento de botón para nuestros consumidores de TypeScript.

Algunos ejemplos de esta verificación de tipos en acción:

// Pass - e is typed as
// `React.MouseEventMouseEvent>`
<Button onClick={(e) => { console.log(e) }} />

// Pass - aria-label is typed
// as `string | undefined`
<Button aria-label="My button" />

// Fail - type "input" is not
// assignable to `"button" |
// "submit" | "reset" | undefined`
<Button type="input" />

Patrón 4: Valores predeterminados con opiniones

Para ciertos componentes, es posible que desee asignar atributos predeterminados a valores específicos. Ya sea para reducir errores o mejorar la experiencia del desarrollador, proporcionar un conjunto de valores predeterminados es específico para una organización o equipo. Si encuentra la necesidad de predeterminar ciertas props, debe asegurarse de que aún sea posible para los consumidores anular esos valores si es necesario.

Una complejidad común que se encuentra con los elementos button es el tipo de valor predeterminado, "submit". Este tipo predeterminado a menudo envía formularios circundantes accidentalmente y conduce a escenarios de depuración difíciles. A continuación, le mostramos cómo establecemos el atributo "button" de forma predeterminada.

Actualicemos el componente Button para devolver un botón con el tipo actualizado.

// File: src/buttons/Button.tsx

return (
  <button
    ref={ref}
    type="button"
    {...props}
  >
    {children}
  </button>
);

Al colocar las props predeterminadas antes de la difusión de props, nos aseguramos de que cualquier valor proporcionado por los consumidores tenga prioridad.

Mira algunas bibliotecas de código abierto

Si está creando una biblioteca de componentes para tu equipo, eche un vistazo a las bibliotecas de componentes de código abierto más populares para ver cómo utilizan los patrones anteriores. Aquí hay una lista de algunas de las principales bibliotecas de componentes de React de código abierto para examinar:

Hasta la próxima.

Redux explicado de manera simple y sucinta para los desarrolladores de React

Redux es una biblioteca de administración de estado ampliamente utilizada para aplicaciones React y TypeScript. Es más fácil que nunca administrar el estado en React gracias al useState React Hook, así como a la API de contexto. Sin embargo, cuando su base de código crezca mucho, necesitará una solución de administración de estado más potente y estructurada, en lugar de ad-hoc. Ahí es donde Redux puede ayudar.

¿Por qué necesitas Redux?

Cuando trabajas con React, generalmente terminas con un estado que se usa globalmente en toda la aplicación.

Uno de los enfoques para compartir el estado en todo el árbol de componentes es usar la API de contexto. A menudo, lo usamos en combinación con hooks como useReducer y useState para administrar el estado global de la aplicación.

Este enfoque funciona, pero solo puede llevarte hasta cierto punto. Al final, debes inventar tus propias formas de administrar los side-effects, depurar y dividir código de administración de estado en módulos para que no se convierta en un desastre incomprensible.

Una mejor idea es utilizar herramientas especializadas. Una de esas herramientas para administrar el estado de la aplicación global es Redux.

Cómo funciona Redux

Redux es un marco de gestión de estado que se basa en la idea de representar el estado global de la aplicación como una función reductora.

En Redux, para administrar el estado, definimos una función que acepta dos argumentos: state, para el estado anterior, y action, el objeto que describe la actualización del estado.

function reducer(state = '', action: Action) {
  switch (action.type) {
    case 'SET_VALUE':
      return action.payload
    default:
      return state
  }
}

Este reductor representa un valor de tipo cadena. Maneja solo un tipo de acción: SET_VALUE.

Si el tipo de campo de acción recibido no es SET_VALUE, el reductor devuelve el estado sin cambios.

Después de tener el reductor, creamos la store(tienda) usando el método redux createStore.

const store = createStore(
  reducer,
  'Initial Value'
)

La store proporciona un método de suscripción que nos permite suscribirnos a las actualizaciones de la store.

store.subscribe(() => {
  const state = store.getState()
  console.log(state)
})

Aquí, le hemos pasado un callback que registra el valor del estado en la consola.

Para actualizar el estado, despachamos(dispatch) una acción:

store.dispatch({
  type: 'SET_VALUE',
  payload: 'New value'
})

Aquí pasamos un objeto que representa la acción(action). Se requiere que cada acción tenga el campo de type y opcionalmente, payload.

Por lo general, en lugar de crear acciones en el lugar, las personas definen action creator functions:

const setValue = (value) => ({
  type: "SET_VALUE",
  payload: value
})

Y esta es la esencia de Redux.

¿Por qué no podemos usar el hook useReducer en lugar de Redux?

Desde la versión 16.8, React admite Hooks. Uno de ellos, useReducer, funciona de manera muy similar a Redux.

Es fácil administrar el estado de la aplicación usando una combinación de useReducer y React Context API.

Entonces, ¿por qué necesitamos Redux si tenemos una herramienta nativa que también nos permite representar el estado como un reductor? Si lo ponemos a disposición en toda la aplicación mediante la API de contexto, ¿no será suficiente?

Redux ofrece algunas ventajas importantes:

  • Herramientas del navegador: pueded usar Redux DevTools para depurar tu código Redux. Est nos permite ver la lista de acciones enviadas, inspeccionar el estado e incluso viajar en el tiempo. Puede alternar en el historial de acciones y ver cómo el estado se ocupó de cada uno de ellos.
  • Manejo de efectos secundarios: con useReducer, debes inventar tus propias formas de organizar el código que realiza las solicitudes de red. Redux proporciona la API de middleware para manejar eso. Además, existen herramientas como Redux Thunk que facilitan aún más esta tarea.
  • Pruebas: como Redux se basa en funciones puras, es fácil de probar. Todas las pruebas se reducen a verificar la salida con las entradas dadas.
  • Patrones y organización de código: Redux está bien estudiado y hay recetas y mejores prácticas que puede aplicar. Existe una metodología llamada Ducks que puedes usar para organizar el código Redux.

Construyendo con Redux

Ahora que ha visto ejemplos de lo que hace Redux y cómo funciona, está listo para usarlo en un proyecto real. En la próxima lección, lo guiaré a través de la creación de un proyecto de React y TypeScript con Redux.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment