React: Métodos del ciclo de vida de un componente

React: Métodos del ciclo de vida de un componente

Introducción y diagrama

Antes de todo, estoy en el proceso de aprendizaje de React y en esta publicación voy a explicar, según mi entendimiento, los métodos del ciclo de vida de un componente con React.

A partir de la versión 16.3 de React se agregaron nuevos métodos del ciclo de vida de un componente para mejorar el rendimiento, buenas practicas y así obtener una mejor calidad de los componentes creados.

Principalmente este cambio es debido a componentes con funcionalidad asíncrona, esto es muy importante porque normalmente el mundo real es asíncrono y los componentes que creamos son utilizados en el mundo real por personas.

Por esta razón también se empiezan a dejar de utilizar los siguientes métodos, esto sucederá a partir de la versión 17 de React:

  • componentWillMount()
  • componentWillRecieveProps(nextProps)
  • componentWillUpdate(nextProps, nextState)

Dado que los anteriores métodos se dejaran de usar en la version 17, los siguientes métodos son los recomendados a utilizar en un componente:

Montado:

  • constructor()
  • static getDerivedStateFromProps(nextProps, prevState)
  • render()
  • componentDidMount()

Actualización:

  • static getDerivedStateFromProps(nextProps, prevState)
  • shouldComponentUpdate(nextProps, nextState)
  • getSnapshotBeforeUpdate(prevProps, prevState)
  • render()
  • componentDidUpdate(prevProps, prevState, snapshot)

Desmontado:

  • componentWillUnmount()

Si ordenamos los métodos de manera secuencial:

  • constructor()
  • static getDerivedStateFromProps(nextProps, prevState)
  • render()
  • shouldComponentUpdate(nextProps, nextState)
  • getSnapshotBeforeUpdate(prevProps, prevState)
  • render()
  • componentDidUpdate(prevProps, prevState, snapshot)
  • componentWillUnmount()

Y para visualizar su relación, aquí está un diagrama de flujo:

Diagrama de los métodos del ciclo de vida de un componente en React
Diagrama de los métodos del ciclo de vida de un componente en React

Si observamos el diagrama, el método static getDerivedStateFromProps(nextProps, prevState) sustituye al método deprecado componentWillReceiveProps(nextProps), también parece ser que el método getSnapshotBeforeUpdate(prevProps, prevState) sustituye al método deprecado componentWillUpdate(nextProps, nextState).

Para fines de demostración vamos a crear un componente Padre y otro componente Animal (componente hijo), lo ideal es que el componente Padre maneje todo el state, pero para demostrar el funcionamiento de los métodos ocuparemos algo de state en nuestro componente Animal.

constructor(props)

El constructor es un método de la mayoría de los lenguajes de programación orientada a objetos, y se utiliza para crear la instancia de una clase. En react el constructor se usa para crear la instancia de nuestro componente.

Cabe mencionar que después de la ejecución de este método, nuestro componente aún no se pinta en nuestro navegador, al proceso de pintado, es decir, insertarlo en el DOM, se le llama Montar o Mount en ingles.

Como buena practica de programación es importante ejecutar super() dentro de un constructor para que realice cualquier llamada a constructores padres.

En el caso de react se debe llamar con las props recibidas en el constructor, o sea, super(props), esto nos permite poder acceder a las props a través de this.props dentro del constructor.

El constructor se usa normalmente para las siguietes dos cosas:

  • Definir el estado local con un componente a través de this.state.
  • Para enlazar el objeto this(la instancia de nuestro componente) a los métodos que son utilizados en el método render(). Estos métodos son usados como manejadores de eventos

El estado de nuestro componente en el constructor se define así:

constructor (props) {
    super(props);
    this.state = {
        propiedad: 'Algún valor'
    }
}

Si no defines ningún estado en el constructor, entonces no lo necesitas.

Si se te ocurre definir el state usando las props pasados como parámetros probablemente es mejor definir el state en un componente padre o en la raíz de todos los componentes porque el estado no estará sincronizado con los cambios de las propiedades.

constructor (props) {
    super(props);
    this.state = {
        propieda: props.nombrePropiedad
    }
}

Para enlazar la referencia de la instancia de nuestro componente a los métodos que son utilizados en el método render() y que normalmente son los manejadores de eventos:

class Padre extends React.Component {
    constructor (props) {
        super(props);
        this.state = { src: '' }
        this.cambiarAnimal = this.cambiarAnimal.bind(this);
    }

    cambiarAnimal () {
        this.setState({
            src: 'Algúna url que apunte a una imagen de un animal'
        });
    }

    render() {
        return (
          <div>
            <Animal src={this.state.src}/>
            <button onClick={this.cambiarAnimal}>Cambiar animal</button>
          </div>
        );
    }
}

Ahora el método this.cambiarAnimal podrá acceder a la instancia de nuestro componente a través de this y así utilizar this.setState() para cambiar el estado.

Existe otra opción para utilizar métodos de nuestra clase como manejadores de eventos, con el uso de funciones flecha (arrow functions).

class Padre extends React.Component {
    constructor (props) {
        super(props);
        this.state = { src: '' }
    }

    cambiarAnimal = () => {
        this.setState({
            src: 'Algúna url que apunte a una imagen de un animal'
        });
    }

    render() {
        return (
          <div>
            <Animal src={this.state.src}/>
            <button onClick={this.cambiarAnimal}>Cambiar animal</button>
          </div>
        );
    }
}

static getDerivedStateFromProps(nextProps, prevState)

Este método es estático, sí, debe tener el modificador static que indica que este método no está enlazado a alguna instancia del componente, sino más bien a su clase. Se invoca después de instanciar un componente y también cuando el componente recibe cambios en las propiedades.

Debe tener siempre un valor de retorno, ya sea un objeto para actualizar el state o null si no se quiere actualizar el state en relación con los nuevos valores de las props recibidas. Es importante saber que este método se ejecuta también cuando un componente padre provoca que el componente hijo sea de nuevo renderizado, por esta razón debes comparar valores anteriores con los nuevos para evitar mandar a actualizar el state cuando no hubo realmente un cambio.

Podemos razonar que este método nos puede servir para mantener sincronizado nuestro state (o solo una parte) con las props pasadas desde un componente padre.

Por el momento en nuestro ejemplo del componente Animal solo visualizaremos los datos y regresaremos null porque no queremos actualizar el estado, además el atributo src de nuestra imagen se actualiza cuando la propiedad src del componente cambia.

...
static getDerivedStateFromProps (nextProps, prevState) {
    console.log('nextProps: ', nextProps);
    console.log('prevState: ', prevState);
    return null;
}

render() {
    return (
        <img className="cat__img"
          src={this.props.src}
          />
     );
}
...

Lo siguiente realmente no es necesario, pero para visualizar la ejecución de este método supongamos que dentro de nuestro componente Animal vamos a manejar la url(src) de la imagen del animal en this.state.src, así:

constructor (props) {
    this.state = {
        src: props.src
    };
}

static getDerivedStateFromProps (nextProps, prevState) {
    if (prevState.src !== nextProps.src) {
      // necesario para actualizar la imagen cada vez que cambie this.props.src
      return { src: nextProps.src };
    }

    return null;
}

render() {
    return (
        <img className="cat__img"
          src={this.state.src}
          />
     );
}
...

Ahora prueba el código aquí, y revisa los mensajes de la consola, por el momento solo nos estamos enfocando en el constructor(props) y static getDerivedStateFromProps(nextProps, prevState):

render()

Este método es obligatorio en cualquier componente, pues como su nombre lo dice, se utiliza para obtener los elementos finales a visualizar o pintar en el navegador. Debe ser una función pura, es decir, no debe modificar las props, no debe modificar el state ni realizar operaciones del DOM.

Según mi entendimiento, el resultado de este método es utilizado por ReactDOM.render() para insertarlo en el DOM del navegador. Si el componente en cuestión ha sido insertado previamente, solo se muta el DOM lo necesario para reflejar los nuevos cambios, esto quiere decir que render() regresa los objetos necesarios para que en otro lugar sean insertados en el DOM.

Esto se puede comprobar si observas la consola del anterior ejemplo y luego das clic sobre el botón “Cambiar animal” entonces verás que el método render() es ejecutado antes de getSnapshotBeforeUpdate() y componentDidUpdate().

Con esto tengo una duda, ¿En qué momento se modifica el DOM?, yo creo que se modifica el DOM después de ReactDOM.render() y antes de que componentDidUpdate().

...
render() {
    return (
        <img className="cat__img"
          src={this.state.src}
          />
     );
}
...

componentDidMount()

Este método se ejecuta cuando nuestro componente está listo en el DOM, siguiendo el razonamiento explicado en el método render(), se ejecuta después de que React inserte el DOM, y antes del método `render()`. Por eso es útil para realizar llamadas ajax y operaciones con el DOM como agregar eventos y/o modificar elementos internos.

Dentro de este método es seguro cambiar el state, pero si ejecutamos this.setState() provocara que nuestro componente se vuelva a renderizar.

La documentación oficial de React nos advierte tener cuidado con esto, pues puede causar problemas de rendimiento por renderizar nuestro componente varias veces. Sin embargo es necesario para los casos de tomar medidas y posiciones de algunos elementos antes de renderizar, por ejemplo el tamaño y posición de modales y tooltips.

Para ver el uso de este método veamos el siguiente ejemplo, si revisamos la consola veremos que render() se ejecuta dos veces, también si damos clic en el botón Cambiar animal, se nota que de nuevo render() se ejecuta dos veces.

¿Por qué sucede esto?, sucede porque dentro del método componentDidMount() agregamos un escuchador de eventos para la carga de la imagen del animal, al ejecutarse this.onImgLoad().

`this.onImgLoad()` invoca a this.setState() y esta función provoca que el componente se vuelva a renderizar para mostrar las medidas exactas de la imagen cuando se termina de cargar.

shouldComponentUpdate(nextProps, nextState)

En versiones actuales de React, este método se ejecuta para decidir si los cambios en las props o el state merecen que se vuelva a renderizar el componente con estos nuevos datos.

El valor de retorno de esta función es true o false. Si el resultado de este método es true, los métodos render(), getSnapshotBeforeUpdate() y componentDidMount()no se ejecutan.

Recibe como parámetros las nuevos valores pros y del state, con estos valores y los valores actuales de nuestro componente podemos condicionar si es necesario volver a renderizar o no, de esta manera podemos mejorar el rendimiento manualmente.

Si no implementamos este método en nuestro componente, React toma como resultado el valor true, por lo que siempre se volverá a renderizar. El resultado no influye en los componentes hijos, si en un componente padre el resultado es false, esto no impide que componentes hijos necesiten volver a ser renderizados.

Es importante mencionar que en la documentación indica que tal vez en futuras versiones de React, este método no impida un renderizado del componente, o sea, que en un futuro si este método regresa falseaun así se ejecutaran los métodos render(), getSnapshotBeforeUpdate() y componentDidUpdate().

Esto último me deja con un sabor amargo, actualmente este método es un buen lugar para mejorar el rendimiento de nuestro componente porque evitamos el re-renderizado en situaciones que no sean necesarias, pero después en futuras versiones cabe la posibilidad de perder esta habilidad, entonces quiero pensar que deben existir otras maneras de mejorar este caso de rendimiento, ¿Alguien tiene alguna idea?.

Veamos un ejemplo, en el anterior método componentDidMount() mencionamos que agregamos un escuchador de eventos al finalizar la carga de la imagen para poder obtener sus medidas, estás medidas las mostramos en nuestro componente, pero esta el caso de dos imágenes de perros que tiene la misma medida, 128px - 128px, entonces el ancho y el alto de la imagen no cambia, por lo que no es necesario volver a renderizar nuestro componente.

Si cambiamos de entre el perro de raza chihuahua y el perro ladrando, podemos ver en la consola que el último método ejecutado es el shouldComponentUpdate(), como el resultado fue false, no se ejecutaron de nuevo los métodos render(), getSnapshotBeforeUpdate() y componentDidUpdate().

getSnapshotBeforeUpdate(prevProps, prevState)

Este método se ejecuta después de render() y antes de componentDidUpdate(), el valor que regresa la ejecución de este método se convierte en el tercer parámetro llamado snapshot de componentDidUpdate(prevProps, prevState, snapshot), este método debe regresar algún valor o null, si no es así, React nos advertirá con algo parecido al siguiente warning en la consola:

Warning:
Animal.getSnapshotBeforeUpdate(): A snapshot value (or null) must be returned. You have returned undefined.

Además recibe las props y el state antes de la actualización por lo que es fácil hacer comparaciones con las props y el state actuales a través de this.props y this.state.

Este método puede regresar cualquier tipo de valor, por ejemplo puede regresar datos del DOM como cuantos elementos existen en una determinada lista de elementos o la posición del scroll antes de que el componente sea actualizado a través de ReactDOM.render()(si nuestra hipótesis explicada en el método render() es correcta).

Si revisamos el código de getSnapshotBeforeUpdate() y componentDidUpdate() y además revisamos la consola, notaremos que getSnapshotBeforeUpdate() le envía a componentDidUpdate el numero de elementos del DOM que contiene el componente en cuestión, que son 2, el párrafo con las medidas de la imagen y la imagen del animal.

componentDidUpdate(prevProps, prevState, snapshot)

Este método se ejecuta cuando el componente ha sido actualizado totalmente, y es reflejado en el DOM de nuestra aplicación, recibe las props y el state antes de la actualización por lo que es fácil hacer comparaciones con las props y el state actuales a través de this.props y this.state.

Aquí se puede trabajar con el DOM del componente dado que este mismo ha sido actualizado, además se puede realizar operaciones como obtener datos remotos según los cambios de las props o state.

No se ejecuta la primera vez que se usa el método render(), es decir, cuando se monta el componente.

En la consola podemos ver los datos, prevProps, preState, y snapshot, este último tiene el valor 2. También vemos this.props y this.state

componentWillUnmount()

Este método se ejecuta justo antes del que el componente sea desmontado del DOM, es un buen lugar para liberar recursos y memoria. Un ejemplo claro es la eliminación de escuchadores de eventos que ya no se van a necesitar, también se pueden cancelar peticiones remotas que se estén ejecutando actualmente dado que estas seguirán su proceso aún desmontando el componente.

En el ejemplo de abajo se ilustra como se elimina un escuchador de evento load para la carga de la imagen del animal.

Introducción a programación funcional

Introducción a programación funcional

Origen

El origen de la programación funcional data del año 1936, cuando un matemático llamado Alonzo Church necesitaba resolver un problema complejo, del cual no hablaremos aquí, pero Alonzo creó una solución, el cálculo lambda. De esta forma nació la programación funcional, incluso mucho antes de la primera computadora digital programable y de los primeros programas escritos en ensamblador.

La programación funcional es mucho más antigua que la programación estructurada y la programación orientada a objetos. Te recomiendo también revisar esta información sobre el paradigma de programación funcional.

Sin efectos colaterales

Todo lo que necesitas saber sobre programación funcional es que “No tiene efectos colaterales“, es decir, que sus elementos (funciones) son inmutables.

Lo anterior significa que una función no se mete en lo absoluto con datos que existen fuera de ella, si puede utilizar los datos enviados como parámetros, pero no debe modificarlos, todos los datos deben ser inmutables, ¿Por qué?, pues porque así hacemos las cosas más simples y con menos errores, más adelante tenemos un ejemplo. A este comportamiento se le llama puro.

Sin efectos colaterales nos permite entender todas las demás características de la programación funcional, por mencionar algunas principales:

  • Funciones puras
  • Inmutabilidad
  • Funciones de alto nivel
  • Funciones currificadas
  • Recursividad
  • Menos errores de programación porque no realiza efectos colaterales.

Toda función teniendo ciertos parámetros, cuando se ejecuta, obtendremos un resultado basado en esos parámetros, y si volvemos a utilizar los mismos parametros, entonces la función regresara el mismo valor. Esto es debido a que una función no depende de más datos externos que puedan ser modificados, así tendremos más seguridad y nuestro código se vuelve más entendible.

Javascript es un lenguaje de programación muy potente y multiparadigma, soporta el paradigma funcional y lo utilizaremos para nuestros ejemplos.

Funciones puras e inmutabilidad

Bueno, realmente ya explicamos de manera indirecta que es una función pura en los párrafos anteriores, pero veamos una definición formal. Una función pura es la que cumple con estas dos características:

  1. Siempre obtendremos el mismo resultado dado los mismos parámetros de entrada.
  2. No tiene ningún efecto colateral observable, no modifica el estado de los datos externos, por lo que todos los datos deben ser inmutables.

Un ejemplo de como cambiar el nombre de una objeto persona usando primero una función con efector colaterales:

function cambiarNombre(nombre) {
  persona.nombre = nombre;
}
const jaime = { nombre: 'Jaime', edad: 30 };
cambiarNombre('Juan')
console.log(jaime); // { nombre: 'Juan', edad: 30 }

En la anterior función notamos que se cambia objeto jaime, el cual supongamos que es parte del estado de nuestra aplicación, pero puede haber otras funciones que utilicen este objeto, por lo que se puede generar problemas si alguien más espera que la propiedad nombre del objeto siga siendo 'jaime'.

Ahora veamos la versión funcional de la función:

function cambiarNombre(nombre, persona) {
  return {
    nombre: nombre,
    edad: persona.edad
  }
}
const jaime = { nombre: 'Jaime', edad: 30 };
const juan = cambiarNombre('Juan', jaime);
console.log(jaime); // { nombre: 'Jaime', edad: 30 }
console.log(juan); // { nombre: 'Juan', edad: 30 }

En la versión funcional, cambiarNombre no modifica el objeto jaime, más bien crea un nuevo objeto con la misma edad que jaime y con la propiedad nombre igual a 'Juan', con esto evitamos efectos colaterales por si el objeto jaime es utilizado por otra función u otro programador.

Con esta función sin efecto colateral, nos damos cuenta de que los datos se manejan sin ser modificados, es decir, inmutables, los parámetros persona y nombre nunca fueron cambiados.

Funciones de alto nivel y currificadas

Una función de alto nivel es una función que implementa al menos una de las opciones siguientes:

  • Recibir como parámetro una o más funciones
  • Regresar como resultado una función

Un ejemplo muy común usado en nuestro navegador web es agregar escuchadores de eventos:

<button id="boton">Soy un botón</button>
const boton = document.querySelector('#boton');
boton.addEventListener('click', function () {
  alert('Click sobre el boton');
});

En el código pasamos como segundo parámetro del método addEventListener una función anónima (sin nombre) que mostrará un alerta al dar click sobre un botón con id igual a ‘botón’. Dado que pasamos por parámetro una función, entonces se dice que es una función de alto nivel.

Otro ejemplo de función de alto nivel lo podemos observar en los métodos de arreglos en Javascript, el código de abajo toma un arreglo de números y crea otro arreglo con los valores aumentados al doble.

See the Pen Funciones de alto nivel by Jaime Cervantes Velasco (@jaime_cervantes_ve) on CodePen.

La función map retorna un nuevo arreglo, no cambia el arreglo original, por lo que decimos que no existe un efecto colateral.

Las funciones currificadas son funciones de alto nivel que como resultado regresan otra función, de tal manera que el acto de currificar es convertir una función de más de un parámetro en dos o más funciones que reciben parcialmente esos parámetros en dos o más invocaciones currificadas.

See the Pen Funciones de alto nivel y funciones currificadas by Jaime Cervantes Velasco (@jaime_cervantes_ve) on CodePen.

Las funciones flecha o arrow functions nos permiten acceder al contexto de los parámetros, es por eso que seguimos teniendo acceso al parámetro a de la primera invocación de summaryCurry. De esta manera cuando se les define un valor, una arrow function puede ver ese valor. Para lograr lo mismo sin funciones flecha se debe utilizar la variable arguments que existe como variable local dentro de todas las funciones en JavaScript. Para mantener las cosas simples, de momento no veremos como hacerlo con arguments.

Función recursiva

Para crear una función recursiva, primero se define un caso base, luego a través de la división del problema en pedazos más pequeños se encuentra un patrón, este patrón llamado caso recursivo se repite muchas veces, es aquí donde la función se llama así misma y acumula los resultados, la ejecución se detiene hasta llegar a su caso base.

  1. El caso base, el cual permite detener la ejecución de subsecuentes invocaciones de la función recursiva.
  2. El caso recursivo, el cual permite que una función se llame a sí misma hasta llegar al caso base.

El factorial de un número positivo es la multiplicación de ese número por el número inmediato menor y así sucesivamente hasta llegar al número 1, su notación es n!, donde n es un número positivo. Por ejemplo el factorial de 5 es 120, 5! = 5 x 4 x 3 x 2 x 1 = 120.

// caso base:
1! = 1 = 1
// caso recursivo, ejemplos:
2! = 2 x 1 = 2 x 1!
3! = 3 x 2 x 1 = 3 x 2!
4! = 4 x 3 x 2 x 1 = 4 x 3!
5! = 5 x 4 x 3 x 2 x 1 = 5 x 4!

Con estos datos, podemos crear nuestra formula factorial(n) = n * factorial(n-1), lo cual seria nuestro caso recursivo, pero debemos de añadir nuestro caso base para que se detenga, cuando n=1 debemos obtener como resultado 1.

Veamos como quedaría nuestra función recursiva en javascript:

See the Pen Funciones recursivas by Jaime Cervantes Velasco (@jaime_cervantes_ve) on CodePen.

Con esto cubrimos las cualidades principales de la programación funcional, pero recuerda, lo más importante es que NO existen efectos colaterales.

Patrón de diseño observador en Javascript

Patrón de diseño observador en Javascript

¿Qué es el patrón de diseño observador?

El patrón de diseño observador, define una dependencia de uno a muchos objetos de tal manera que cuando un objeto cambio de estado, todos sus dependientes son notificados y actualizados automáticamente

Del libro “Design patterns: elements of reusable object-oriented software”

Si aún no sabes que es un patrón de diseño, revisa primero esta publicación sobre patrones de diseño.

Si has utilizado eventos del navegador, como escuchar el evento click sobre un botón, ahí estás utilizando el patrón observador, tu función callback es tu objeto observador y el botón es el sujeto, tu función callback esta interesada en la actividad de dar click sobre el botón.

Se compone de un Sujeto y uno o más Observadores. Veamos el diagrama tomado del libro Design Patterns: Elements of reusable Object-Oriented software:

Patrón de diseño observador
Patrón de diseño observador

El Sujeto está compuesto por las siguientes métodos importantes que usaremos en nuestro código javascript:

  • Una colección de observers
  • Método attach()
  • Método detach()
  • Método notify()

Normalmente un Observador contiene un método update() que el Sujeto utiliza.

Para tener más claro esto, vamos a ver dos ejemplos del patrón observador, el primero será el Observador puro y el segundo en su modo Publicador/Suscriptor.

Simplificando el observador

Simplificando el diagrama anterior, el patrón observador funciona así:

Patrón de diseño observador simplificado
Patrón de diseño observador simplificado

Juega con el ejemplo de abajo, y agrega nuevos checkbox, si has agregado más de un nuevo checkbox, podemos notar que cuando damos click en el checkbox ‘Seleccionar‘ todos los demás checkbox nuevos se enteran de esa actividad y se actualizan automáticamente.

Todo esto sin necesidad de estar al pendiente de que el checkbox ‘seleccionar‘ ha cambiado su estado, el encargado de notificar del cambio es el Sujeto y solo si existe algún cambio. Esto es muy eficiente si tienes cientos o miles de checkbox observando ese cambio.

Si das click sobre uno de los checkbox, se ejecuta el método subject.detach(), entonces ese checkbox ya no es un observador, por lo que si ahora activas y desactivas el checkbox seleccionar nunca es notificado sobre el cambio.

Publicador/suscriptor simple

El patrón de diseño publicador/suscriptor es una variación del observador, en este modo el suscriptor u observador se suscribe a una actividad o evento del publicador o sujeto.

El publicador notifica a todos los objetos suscritos cuando el evento al que están interesados se dispara o publica.

El publicador está compuesto por las siguientes métodos importantes que usaremos en nuestro código javascript:

  • Una colección de observers o subscribers
  • Método subscribe() o attach()
  • Método unsubscribe() o detach()
  • Método publish() o notify()

Aquí dejo el diagrama simple de como funciona esta versión:

Patrón de diseño publicador/suscriptor simplificado
Patrón de diseño publicador/suscriptor simplificado

El publicador/suscriptor se parece más a los eventos del DOM y a eventos personalizados, pero no deja de ser el patrón de diseño Observador. Ademas, como observadores, podemos suscribirnos a más de una actividad o evento.

En el ejemplo, el método suscribe agrega la función observadora, esta última se ejecuta cuando la actividad click es publicada.

publisherSeleccionar.subscribe('check', subscriberCheckbox.update, subscriberCheckbox);

En conclusión, el patrón de diseño observador es muy útil, se puede modificar o adecuarlo a ciertas necesidades, un ejemplo es la variacion publicador/suscriptor. Se usa en:

  • ReactveX.
  • Librerias y frameworks de componentes web tales como angular, react, lit, vue, svelte utilizan este patrón para su bindeo de datos.
  • Navegadores web cuando usamos eventos
  • Node.js cuando utilizamos EventEmitter al recibir una petición a nuestro servidor o al leer un archivo del disco duro.
  • Flux y redux para manejar cambios en el estado de nuestra aplicación.
¿Cómo crear un servidor web en 5 minutos con Node.js?

¿Cómo crear un servidor web en 5 minutos con Node.js?

Para crear un servidor web en node.js, primero, ¿Qué es node.js?, tomando la definición del sitio oficial, Node.js® es un entorno de ejecución para JavaScript construido con el motor de JavaScript V8 de Chrome. Node.js usa un modelo de operaciones E/S sin bloqueo y orientado a eventos, que lo hace liviano y eficiente. El ecosistema de paquetes de Node.js, npm, es el ecosistema más grande de librerías de código abierto en el mundo.

Vamos a describir la parte que nos interesa, Node.js es un programa, V8 es un motor de javascript de código abierto creado por Google, por lo que también lo hace un programa, V8 está escrito en C++, y la tarea de V8 es tomar código Javascript y convertirlo a código máquina (compilar código), pero lo que lo hace especial para nuestros fines es que puede ser embebido dentro de otros programas, lo que permite que V8 esté embebido en Node.js, V8 por así decirlo es el punto de partida para toda la funcionalidad de Node.js.

Node.js también está escrito en C++ y utiliza la API de V8 para agregarle características y funcionalidades nuevas a Javascript. Estas nuevas funcionalidades permiten tener acceso al sistema de archivos y carpetas, nos permite crear un servidor TCP y http, además de acceso a POSIX, o sea, a toda la funcionalidad del sistema operativo donde se encuentre instalado.

Node.js proporciona la sintaxis Javascript para crear programas que tengan acceso a las características del sistema operativo donde sé está ejecutando.

Con esto podemos razonar que con Node.js podemos crear un servidor web, para crearlo, vamos a utilizar NPM (Node Package Manager) y express.js un frawework web.

Vamos a crear una nueva carpeta llamada mi-servidor-web, luego accede a esta carpeta con:

$ cd mi-servidor-web

Ahora vamos a iniciar el proyecto utilizando el siguiente comando:

$ npm init

La línea de comandos nos pedirá algunos datos, puedes dar “enter” a todos si quieres, te muestro un ejemplo:

Press ^C at any time to quit.
package name: (mi-servidor-web)
version: (1.0.0)
description: Mi primer servidor web
entry point: (index.js)
test command:
git repository:
keywords:
author: Jaime Cervantes<jaime.cervantes.ve@gmail.com>
license: (ISC)
About to write to /home/jaime/develop/node.js/mi-servidor-web/package.json:
{
  "name": "mi-servidor-web",
  "version": "1.0.0",
  "description": "Mi primer servidor web",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Jaime Cervantes <jaime.cervantes.ve@gmail.com>",
  "license": "ISC"
}
Is this ok? (yes)

npm init nos genera un archivo package.json:

{
  "name": "mi-servidor-web",
  "version": "1.0.0",
  "description": "Mi primer servidor web",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Jaime Cervantes <jaime.cervantes.ve@gmail.com>",
  "license": "ISC"
}

Este archivo contiene la información anteriormente proporcionada y además se encarga de controlar los paquetes que instalamos para nuestro proyecto. Por ejemplo, para poder crear nuestro servidor rápidamente, vamos a instalar un paquete llamado express.js de la siguiente manera:

$ npm install express --save

Este comando instala express.js y además actualiza nuestro archivo package.json gracias al parámetro --save en la propiedad dependencies:

{
  "name": "mi-servidor-web",
  "version": "1.0.0",
  "description": "Mi primer servidor web",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Jaime Cervantes <jaime.cervantes.ve@gmail.com>",
  "license": "ISC",
  "dependencies": {
    "express": "^4.16.2"
  }
}

Ya teniendo express instalado, vamos a crear nuestro servidor web creando el archivo  ./mi-servidor-web/index.js:

const express = require('express');
const app = express();
app.use(express.static(__dirname + '/public/'));
app.listen('3000', function() {
  console.log('Servidor web escuchando en el puerto 3000');
});

Hay una parte importante que nos permitirá ver el funcionamiento de nuestro servidor web:

app.use(express.static(__dirname + '/public/'));

Esta línea le indica a nuestro servidor que cuando un usuario haga una petición de archivos estáticos, por ejemplo, http://localhost:300/index.html enviará como respuesta el contenido de ./public/index.html.

Vamos a crear la carpeta public y el archivo index.html con el editor de nuestro preferencia o en línea de comandos si lo deseas. Agregamos el siguiente contenido a nuestro archivo index.html:

<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Mi primer servidor web</title>
</head>
<body>
  <h1>HOLA, mi primer servidor web</h1>
</body>
</html>

Para ejecutar nuestro servidor nos posicionamos dentro de nuestra carpeta mi-servidor-web y ejecutamos el siguiente comando:

$ node index.js;

Veremos este mensaje en nuestra consola:

Servidor web escuchando en el puerto 3000

Por último abrimos nuestro navegador web y obtendremos nuestro index.html como la imagen de abajo, utilizando http://localhost:3000 o http://localhost:3000/index.html:

¿Cómo crear mi primera página web? CSS y Atributos HTML

¿Cómo crear mi primera página web? CSS y Atributos HTML

En HTML, los atributos juegan un papel importante porque permiten definir cierta configuración o comportamiento a los elementos HTML, permitiendo obtener diferentes resultados.

Esta publicación es la segunda parte de una serie de publicaciones sobre ¿Cómo puedes crear tu primera página web?, aquí puedes encontrar la primera parte.

En la primera parte vimos como tener las etiquetas(tags) básicas para tu página web, ahora veremos que son los atributos y CSS.

Primera recordemos como es una etiqueta o tagcon la siguiente imagen.

Observando la imagen aprendemos la sintaxis para agregar atributos, es la parte de color rojo, en este caso classes el atributo, seguido de un signo de =y luego entre comillas el valor que queremos asignarle.

Veamos los atributos starty reversedpara una lista ordenada:

Otros atributos no necesitan algún valor, porque su solo presencia es suficiente, uno de ellos es el atributo reversedy open.

Ahora debemos tomar en cuenta que no todos los atributos son aplicables a todas las etiquetas, los atributos reversedy startson atributos específicos para listas ordenadas, es decir, la etiqueta <ol></ol>y no tienen ningún efecto en otros elementos.

Con lo anterior en cuenta podemos discernir que existen atributos Globalesy No globales, los atributos Globalestienen efecto sobre todas las etiquetas, mientras los No Globalessolo a un grupo de etiquetas o a veces solo a una etiqueta en específico.

Entender que atributos son globales es de mucha ayuda, aun así no hay prisa en aprenderlos de memoria, siempre que tengas dudas revisa la lista de atributos globales aquí.

¿Qué es CSS?

Recordemos que…

HTML permite crear el contenido de nuestro página o aplicación de manera organizada. CSS se encarga de la apariencia visual del contenido generado por HTML, con CSS podemos establecer el diseño, colores, tamaño de letra, dimensiones de cada elemento, posiciones y alineados, bordes, sombras y demás características de diseño. Y finalmente Javascript te permite interactuar dinamicamente con la aplicación, claro ejemplo del uso de Javascript son las notificaciones de facebook, el chat de este mismo, cuando damos me gusta, creamos una publicación o escribimos un comentario.

La sintaxis principal de CSS se llama regla. Una regla se comforma de 2 partes, selectory declaración.

Un selectorpermite seleccionar uno o más elementos HTML de nuestra página. CSS es otro lenguaje diferente a HTML, así que el selector de ejemplo no llevan los signos mayory menorque, el selector NO es así <p>.

Cada declaraciónpuede tener uno o más pares de propiedad: valor;, cada par de propiedad: valor;realiza un cambio visual en los elementos seleccionados por el selector.

Los atributos también son muy utiles a la ahora de agregar estilos y diseño de una página web, esto se debe a que la sintaxis del selectortambién hace uso de los atributos para seleccionar uno o más elementos.

Los estilos se pueden agregar usando la etiqueta <style></style>, y esta etiqueta puede existir dentro del <body>o <head>.

<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>Mi primera página web</title>
  <!-- Agrgando estilos dentro de HEAD -->
  <style>
    p { font-size: 12px; color: #555555; }
  </style>
</head>
<body>
  <!-- Agreando estilos dentro de BODY -->
  <style>
    p { font-size: 12px; color: #555555; }
  </style>
</body>
</html>

Lo más recomendable es poner nuestros estilos en un archivo aparte con extensión .csse importarlo desde nuestro código html dentro del elemento <head>usando la etiqueta <link>.

<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>Mi primera página web</title>
  <!-- Importando estilos externos -->
  <link rel="stylesheets" href="./ruta/de/archivo/nombre.css">
</head>
<body>
</body>
</html>

Con lo que sabemos de CSS veamos un ejemplo de lo que podemos conseguir:

En la primera regla vemos que el selector es html, con esto sabemos que al elemento html se le van a aplicar estilos. También sabemos que dentro de htmlexisten las demas etiquetas que completan nuestra página como elementos hijos.

Una de las propiedades más comunes es font-size, la cual define el tamaño de la letra del texto. El valor de esta propiedad se define con un número y una unidad, en este ejemplo la unidad es pxque quiere decir pixel.

Al elemento htmlse le llama muchas veces rooto raízy en la mayoría de los navegadores web tiene un font-size: 16pxpor defecto. En este caso el valor por defecto lo estamos disminuyendo 2 pixeles.

html {
  font-size: 14px;
}

Existen más unidades, rem, em, %, vhy vwque veremos a detalle más adelante.

Después tenemos nuestro selector body, en la declaración se define el tipo de letra o fuente con la propiedad font-familyy las fuentes se establecen entre comillas, las únicas fuentes que pueden no llevar comillas porque son un estándar en los navegadores web son serif, sans-serif, monospace, cursivay fantasy.

Cada nombre de fuente es separada por una coma(,), de esta manera el navegador va tomando de izquierda a derecha el tipo de fuente, si no existe la primera fuente en tu computadora, va por la segunda y así sucesivamente hasta encontrar una existente.

body {
  /*
   * ESTO ES UN COMENTARIO:
   * Fuentes estandar disponibles en el navegador:
   * serif, sans-serif, monospace, cursive, fantasy
   * Estas no llevan comillas
   */
  font-family: "Helvetica", "Verdana", "Arial", sans-serif;
}

Ya mencionamos que CSS significa Hoja de Estilo en cascada, la parte de cascadase refieren a la herenciay la precedenciade las reglas que definimos, en el caso de font-familyagregado al elemento body, esta propiedad la heredan todos los elementos hijos, por lo que todo nuestra página tendrá el mismo tipo de letra.

Otra propiedad es line-height, la cual define el espacio vertical de las líneas de un texto, podemos notar su funcionamiento en el primer y segundo párrafo, line-height: 1y line-height: 2respectivamente.

p {
  font-size: 14px;
  line-height: 1;
}

#segundo-parrafo {
  text-align: right;
  line-height: 2;

  margin-top: 2rem;
}

Para seleccionar el segundo párrafo ocupamos el atributoid, este atributo debe ser único por cada elemento, no se debe repetir, este selector siempre selecciona un solo elemento.

Dentro de la declaración de propiedades tenemos una propiedad llamada margin-topesta propiedad define la separación superior del segundo párrafo con el primero.

Algo interesante es que el valor es 2rem, la unidad remse refiere al tamaño de fuente del elemento raíz(root), nuestro elemento raíz es htmly como tamaño de letra le definimos anteriormente con 14pxpor lo que margin-topseria igual a 28px. Esta unidad de medida remnos permite definir tamaños relativos al tamaño de la fuente raíz.

Luego en las listas ordenadas tenemos las propiedades padding-top, padding-right, padding-bottomy padding-left. Estas propiedades definen el espacio que existe entre los bordes de un elemento y su contenido. Dado que el selector es olnos permite seleccionar todas las listas ordenadas de nuestra página.

ol {
  font-size: 1.1rem;

  padding-left: 3rem;
  padding-top: 20px;
  padding-bottom: 20px;
}

Ahora tenemos el selector .contorno, donde se define la propiedad border, el cual nos permite resaltar el borde de un elemento, su sintaxis es el grosor del borde, el tipo y por último el color. Con esta regla podemos definir un borde verde a todos los elementos que tengan dentro del atributoclassel valor contorno.

.contorno {
    border: 1px solid green;
}

Otra propiedad de gran ayuda es list-style-positionla cual define la posición de la numeración o viñetas de una lista. Dado que el selector es li, nos permite seleccionar todas los elementos de listas de nuestra página.

li {
  list-style-position: inside;
}

Más abajo tenemos un selector utilizando el atributoidde la primera lista. En la declaración de propiedades se define el color de fondo con background-color: lightblue.

#primera-lista {
  background-color: lightblue;
}

.green {
  background-color: lightgreen;
}

Luego tenemos otro selector que usa el atributoclass, pero un valor de classsi puede ser utilizado en más de un elemento, esto quiere decir, que este selector nos permite seleccionar todos los elementos de nuestra página web que dentro del atributo classtenga el valor green.

Es importante mencionar que el atributo classpuede tener más de un valor separado por un espacio en blanco.

<ol class="green contorno"></ol>

Por último tenemos un elemento detailscon el atributo opensin ningún valor, su sola presencia indica que ese elemento esta desplegadoo abiertoy cuando está desplegado su color de fondo cambia a azul claro. La notación para atributos que no son idni classes la de corchetes [nombreAtributo].

details[open] {
  background-color: lightblue;
}
es_MXES_MX