Qué significan los Hooks para Vue | Trucos CSS

No debe confundirse con Lifecycle Hooks, los Hooks se introdujeron en React en v16.7.0-alpha, y se lanzó una prueba de concepto para Vue unos días después. Aunque fue propuesto por React, en realidad es un mecanismo de composición importante que tiene beneficios en todos los ecosistemas de marcos de JavaScript, por lo que hoy dedicaremos un poco de tiempo a discutir lo que esto significa.

Básicamente, los Hooks ofrecen una forma más explícita de pensar en patrones reutilizables, una que evita la reescritura en los propios componentes y permite que piezas dispares de la lógica con estado funcionen juntas sin problemas.

El problema inicial

En términos de React, el problema era el siguiente: las clases eran la forma más común de componentes al expresar el concepto de estado. Los componentes funcionales sin estado también eran bastante populares, pero debido al hecho de que solo podían renderizar realmente, su uso se limitaba a tareas de presentación.

Las clases en sí mismas presentan algunos problemas. Por ejemplo, a medida que React se volvió más omnipresente, también lo hicieron los obstáculos para los recién llegados. Para entender React, también había que entender las clases. La vinculación hizo que el código fuera detallado y, por lo tanto, menos legible, y una comprensión de this en JavaScript era necesario. También hay algunos obstáculos para la optimización que presentan las clases, que se analizan aquí.

En términos de reutilización de la lógica, era común usar patrones como accesorios de renderizado y componentes de orden superior, pero nos encontramos en un infierno de implementación de estilo similar a una “pirámide de fatalidad” donde el anidamiento se volvió tan sobreutilizado que los componentes podría ser difícil de mantener. Esto me llevó a despotricar borracho con Dan Abramov, y nadie quiere eso.

Los Hooks abordan estas preocupaciones permitiéndonos definir la lógica con estado de un componente usando solo llamadas a funciones. Estas llamadas a funciones se vuelven más componibles, reutilizables y nos permiten expresar la composición en funciones sin dejar de acceder y mantener el estado. Cuando se anunciaron los ganchos en React, la gente estaba emocionada; puede ver algunos de los beneficios ilustrados aquí, con respecto a cómo reducen el código y la repetición:

Cogí @dan_abramovcódigo de # ReactConf2018 y lo visualizaste para que pudieras ver los beneficios que nos brindan los React Hooks. pic.twitter.com/dKyOQsG0Gd

– Pavel Prichodko (@prchdk) 29 de octubre de 2018

En términos de mantenimiento, la simplicidad es clave, y los Hooks proporcionan una forma única y funcional de abordar la lógica compartida con el potencial de una menor cantidad de código.

¿Por qué Hooks in Vue?

Puede leer esto y preguntarse qué tienen para ofrecer Hooks en Vue. Parece un problema que no necesita solución. Después de todo, Vue no usa predominantemente clases. Vue ofrece componentes funcionales sin estado (si los necesita), pero ¿por qué tendríamos que llevar el estado en un componente funcional? Tenemos mixins para composición donde podemos reutilizar la misma lógica para múltiples componentes. Problema resuelto.

Pensé lo mismo, pero después de hablar con Evan You, señaló un caso de uso importante que me perdí: los mixins no pueden consumir y usar el estado de uno a otro, pero Hooks sí. Esto significa que si necesitamos lógica encapsulada en cadena, ahora es posible con Hooks.

Los ganchos logran lo que hacen los mixins, pero evitan dos problemas principales que vienen con los mixins:

  • Nos permiten pasar de estado de uno a otro.
  • Explican de dónde viene la lógica.

Si usamos más de un mixin, no está claro qué propiedad proporcionó qué mixin. Con Hooks, el valor de retorno de la función documenta el valor que se consume.

Entonces, ¿cómo funciona eso en Vue? Mencionamos antes que, cuando se trabaja con Hooks, la lógica se expresa en llamadas a funciones que se vuelven reutilizables. En Vue, esto significa que podemos agrupar una llamada de datos, una llamada a un método o una llamada calculada en otra función personalizada, y hacer que se puedan componer libremente. Los datos, métodos y cálculos ahora están disponibles en componentes funcionales.

Ejemplo

Repasemos un gancho realmente simple para que podamos entender los bloques de construcción antes de pasar a un ejemplo de composición en Hooks.

useWat?

Bien, aquí tenemos, lo que podríamos llamar, un evento cruzado entre React y Vue. El use El prefijo es una convención de React, por lo que si busca Hooks en React, encontrará cosas como useState, useEffect, etc. Más información aquí.

En la demostración en vivo de Evan, puedes ver a dónde accede useState y useEffect para una función de renderizado.

Si no está familiarizado con las funciones de renderizado en Vue, podría ser útil echarle un vistazo.

Pero cuando trabajemos con Hooks al estilo Vue, tendremos, lo adivinó, cosas como: useData, useComputedetc.

Entonces, para que podamos ver cómo usaríamos Hooks en Vue, creé una aplicación de muestra para que la exploremos.

Sitio de demostración

Repositorio de GitHub

En la carpeta src / hooks, he creado un gancho que evita el desplazamiento en un useMounted enganche y vuelva a activarlo useDestroyed. Esto me ayuda a pausar la página cuando abrimos un cuadro de diálogo para ver el contenido y me permite desplazarme nuevamente cuando terminamos de ver el cuadro de diálogo. Esta es una buena funcionalidad para abstraer porque probablemente sería útil varias veces en una aplicación.

import { useDestroyed, useMounted } from "vue-hooks";

export function preventscroll() {
  const preventDefault = (e) => {
    e = e || window.event;
    if (e.preventDefault)
      e.preventDefault();
    e.returnValue = false;
  }

  // keycodes for left, up, right, down
  const keys = { 37: 1, 38: 1, 39: 1, 40: 1 };

  const preventDefaultForScrollKeys = (e) => {
    if (keys[e.keyCode]) {
      preventDefault(e);
      return false;
    }
  }

  useMounted(() => {
    if (window.addEventListener) // older FF
      window.addEventListener('DOMMouseScroll', preventDefault, false);
    window.onwheel = preventDefault; // modern standard
    window.onmousewheel = document.onmousewheel = preventDefault; // older browsers, IE
    window.touchmove = preventDefault; // mobile
    window.touchstart = preventDefault; // mobile
    document.onkeydown = preventDefaultForScrollKeys;
  });

  useDestroyed(() => {
    if (window.removeEventListener)
      window.removeEventListener('DOMMouseScroll', preventDefault, false);

    //firefox
    window.addEventListener('DOMMouseScroll', (e) => {
      e.stopPropagation();
    }, true);

    window.onmousewheel = document.onmousewheel = null;
    window.onwheel = null;
    window.touchmove = null;
    window.touchstart = null;
    document.onkeydown = null;
  });
} 

Y luego podemos llamarlo en un componente de Vue como este, en AppDetails.vue:

<script>
import { preventscroll } from "./../hooks/preventscroll.js";
...

export default {
  ...
  hooks() {
    preventscroll();
  }
}
</script>

Lo estamos usando en ese componente, ¡pero ahora podemos usar la misma funcionalidad en toda la aplicación!

Dos ganchos, entendiéndose

Mencionamos antes que una de las principales diferencias entre hooks y mixins es que los hooks pueden pasar valores de uno a otro. Veamos eso con un ejemplo simple, aunque un poco elaborado.

Digamos que en nuestra aplicación necesitamos hacer cálculos en un gancho que se reutilizará en otro lugar, y algo más que necesite usar ese cálculo. En nuestro ejemplo, tenemos un gancho que toma el ancho de la ventana y lo pasa a una animación para que sepa que solo se dispara cuando estamos en pantallas más grandes.

En el primer gancho:

import { useData, useMounted } from 'vue-hooks';

export function windowwidth() {
  const data = useData({
    width: 0
  })

  useMounted(() => {
    data.width = window.innerWidth
  })

  // this is something we can consume with the other hook
  return {
    data
  }
}

Luego, en el segundo usamos esto para crear un condicional que dispara la lógica de animación:

// the data comes from the other hook
export function logolettering(data) {
  useMounted(function () {
    // this is the width that we stored in data from the previous hook
    if (data.data.width > 1200) {
      // we can use refs if they are called in the useMounted hook
      const logoname = this.$refs.logoname;
      Splitting({ target: logoname, by: "chars" });

      TweenMax.staggerFromTo(".char", 5,
        {
          opacity: 0,
          transformOrigin: "50% 50% -30px",
          cycle: {
            color: ["red", "purple", "teal"],
            rotationY(i) {
              return i * 50
            }
          }
        },
        ...

Luego, en el propio componente, pasaremos uno al otro:

<script>
import { logolettering } from "./../hooks/logolettering.js";
import { windowwidth } from "./../hooks/windowwidth.js";

export default {
  hooks() {
    logolettering(windowwidth());
  }
};
</script>

¡Ahora podemos componer lógica con Hooks en toda nuestra aplicación! Nuevamente, este es un ejemplo artificial para fines de demostración, pero puede ver lo útil que podría ser para aplicaciones a gran escala para mantener las cosas en funciones más pequeñas y reutilizables.

Planes futuros

Los Vue Hooks ya están disponibles para usar hoy con Vue 2.x, pero aún son experimentales. Estamos planeando integrar Hooks en Vue 3, pero probablemente nos desviaremos de la API de React en nuestra propia implementación. Creemos que React Hooks es muy inspirador y estamos pensando en cómo presentar sus beneficios a los desarrolladores de Vue. Queremos hacerlo de una manera que complemente el uso idiomático de Vue, por lo que todavía queda mucha experimentación por hacer.

Puede comenzar consultando el repositorio aquí. Es probable que los Hooks se conviertan en un reemplazo de los mixins, por lo que, aunque la función aún se encuentra en sus primeras etapas, probablemente sea un concepto que sería beneficioso explorar mientras tanto.

(Nuestro más sincero agradecimiento a Evan You y Dan Abramov por corregir este artículo).

(Visited 1 times, 1 visits today)