ivanbtrujillo / ivanbtrujillo.com

Blog using Next
https://ivanbtrujillo.now.sh
0 stars 0 forks source link

Añadiendo un loader cross-page en NextJS #1

Open ivanbtrujillo opened 4 years ago

ivanbtrujillo commented 4 years ago

date: "2020-04-25" img: "https://res.cloudinary.com/ivanbtrujillo/image/upload/c_fill,h_224,w_429/v1590083428/photo-1588007375246-3ee823ef4851_jbrtfv" summary: Aprenderemos como añadir una barra de carga en NextJS que sea reutilizable en distintas páginas usando un custom hook (useLoading).

Para mostrar una barra de carga mientras cargamos la próxima página, utilizaremos el hook useRouter de NextJS que nos proveerá de una serie de event listeners a los que podemos suscribirnos y hacer acciones en respuesta a un cambio de ruta.

Para mostrar la barra de carga, utilizaremos NProgress:

npm install nprogress

Lo siguiente que haremos es crear un custom hook que llamaremos useLoading. Dentro del hook, nos suscribiremos a los eventos del router. Cuando el path cambie, ejecutaremos NProgress.start() para mostrar la barra de carga y cuando haya terminado de cargarse la ruta, ejecutaremos NProgress.done() para ocultarla.

useLayout.tsx

import * as React from "react";
import { useRouter } from "next/router";
import NProgress from "nprogress";

export const useLoading = () => {
  const router = useRouter();

  React.useEffect(() => {
    const handleStart = () => NProgress.start();
    const handleComplete = () => NProgress.done();

    router.events.on("routeChangeStart", handleStart);
    router.events.on("routeChangeComplete", handleComplete);
    router.events.on("routeChangeError", handleComplete);

    return () => {
      router.events.off("routeChangeStart", handleStart);
      router.events.off("routeChangeComplete", handleComplete);
      router.events.off("routeChangeError", handleComplete);
    };
  });
};

Para mostrar la barra de carga en todas las páginas, necesitamos añadir los estilos de NProgress globalmente. Recuerda importar ese fichero .css en _app.tsx:

/* NProgress */

/* Make clicks pass-through */
#nprogress {
  pointer-events: none;
}

#nprogress .bar {
  background: #29d;

  position: fixed;
  z-index: 1031;
  top: 0;
  left: 0;

  width: 100%;
  height: 2px;
}

/* Fancy blur effect */
#nprogress .peg {
  display: block;
  position: absolute;
  right: 0px;
  width: 100px;
  height: 100%;
  box-shadow: 0 0 10px #29d, 0 0 5px #29d;
  opacity: 1;

  -webkit-transform: rotate(3deg) translate(0px, -4px);
  -ms-transform: rotate(3deg) translate(0px, -4px);
  transform: rotate(3deg) translate(0px, -4px);
}

/* Remove these to get rid of the spinner */
#nprogress .spinner {
  display: block;
  position: fixed;
  z-index: 1031;
  top: 15px;
  right: 15px;
}

#nprogress .spinner-icon {
  width: 18px;
  height: 18px;
  box-sizing: border-box;

  border: solid 2px transparent;
  border-top-color: #29d;
  border-left-color: #29d;
  border-radius: 50%;

  -webkit-animation: nprogress-spinner 400ms linear infinite;
  animation: nprogress-spinner 400ms linear infinite;
}

.nprogress-custom-parent {
  overflow: hidden;
  position: relative;
}

.nprogress-custom-parent #nprogress .spinner,
.nprogress-custom-parent #nprogress .bar {
  position: absolute;
}

@-webkit-keyframes nprogress-spinner {
  0% {
    -webkit-transform: rotate(0deg);
  }
  100% {
    -webkit-transform: rotate(360deg);
  }
}
@keyframes nprogress-spinner {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

Por último, importaremos nuestro custom hook en cada página. Una buena idea es crearte un componente Layout que importe el hook y que reciba el contenido de cada página como children. De esta forma lo reutilzamos de una forma mas cómoda.

import Head from "next/head";
import { useLoading } from "components/useLoading";

type LayoutProps = {
  children: React.ReactChild | React.ReactChild[];
  title: string;
}

export const Layout: React.SFC<LayoutProps> = ({
  children,
  title,
}) => {
  useLoading();
  return (
    <>
      <Head>
        <title>{title}</title>
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1"
        ></meta>
      </Head>
        {children}
    </>
  );
};

Y esto es tódo. Espero que te haya gustado y si tienes alguna duda, no dudes en dejarla en los comentarios o escribirme en twitter a @ivanbtrujillo.

ivanbtrujillo commented 4 years ago

date: "2020-04-28T19:17:03.962Z" user: "Eleazar Hernández" userPicture: "https://pbs.twimg.com/profile_images/1360293706/eli_3718_normal.PNG"

Me ha gustado el post!