Introducci贸n al curso avanzado de React

1

Qu茅 necesitas para este curso y qu茅 aprender谩s sobre React.js

2

Proyecto y tecnolog铆as que usaremos

Preparando el entorno de desarrollo

3

Clonando el repositorio e instalando Webpack

4

Instalaci贸n de React y Babel

5

Zeit es ahora Vercel

6

Linter, extensiones y deploy con Now

Creando la interfaz con styled-components

7

驴Qu茅 es CSS-in-JS?

8

Creando nuestro primer componente: Category

9

Creando ListOfCategories y estilos globales

10

Usar informaci贸n real de las categor铆as

11

Creando PhotoCard y usando react-icon

12

SVGR: de SVG a componente de ReactJS

13

Creando animaciones con keyframes

Hooks

14

驴Qu茅 son los Hooks?

15

useEffect: limpiando eventos

16

useCategoriesData

17

Usando Intersection Observer

18

Uso de polyfill de Intersection Observer e imports din谩micos

19

Usando el localStorage para guardar los likes

20

Custom Hooks: useNearScreen y useLocalStorage

GraphQL y React Apollo

21

驴Qu茅 es GraphQL y React Apollo? Inicializando React Apollo Client y primer HoC

22

Par谩metros para un query con GraphQL

23

Usar render Props para recuperar una foto

24

Refactorizando y usando variables de loading y error

25

Usando las mutaciones con los likes

Reach Router

26

驴Qu茅 es Reach Router? Creando la ruta Home

27

Usando Link para evitar recargar la p谩gina

28

Creando la p谩gina Detail

29

Agregando un NavBar a nuestra app

30

Estilando las p谩ginas activas

31

Rutas protegidas

Gesti贸n del usuario

32

Introducci贸n a React.Context

33

Creaci贸n del componente UserForm; y Hook useInputValue

34

Estilando el formulario

35

Mutaciones para registro

36

Controlar estado de carga y error al registrar un usuario

37

Mutaciones para iniciar sesi贸n

38

Persistiendo datos en Session Storage

39

Hacer like como usuario registrado

40

Mostrar favoritos y solucionar fetch policy

41

Cerrar sesi贸n

Mejores pr谩cticas, SEO y recomendaciones

42

脷ltimos retoques a las rutas de nuestra aplicaci贸n

43

React Helmet

44

Midiendo el performance de nuestra app y usando React.memo()

45

React.lazy() y componente Suspense

46

Usando PropTypes para validar las props

47

PWA: generando el manifest

48

PWA: soporte offline

49

Testing con Cypress

Conclusiones

50

隆Felicidades!

No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Aprende todo un fin de semana sin pagar una suscripci贸n 馃敟

Aprende todo un fin de semana sin pagar una suscripci贸n 馃敟

Reg铆strate

Comienza en:

3D
20H
42M
59S
Curso de React Avanzado

Curso de React Avanzado

Miguel 脕ngel Dur谩n

Miguel 脕ngel Dur谩n

useCategoriesData

16/50
Recursos

Aportes 58

Preguntas 1

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

o inicia sesi贸n.

category/index.js

import React from 'react';
import { ContainerCategorySkeleton, CategoryImage, CategoryTitle } from './styles';

export const CategorySkeleton = props => {
    console.log(props)
    return (
        <ContainerCategorySkeleton>
            <CategoryImage light={props.light} />
            <CategoryTitle light={props.light} />
        </ContainerCategorySkeleton>
    )
}

category/style.js

import styled, { css } from 'styled-components';
import { skeletonStyle } from '../../../styles/skeleton';

export const ContainerCategorySkeleton = styled.div`
    display: flex;
    flex-direction: column;
    align-items: center;
`;

export const CategoryImage = styled.div`
    width: 75px;
    height: 75px;
    border-radius: 50%;
    ${
        props => css`
            ${skeletonStyle(props.light)}
        `
    }
`;

export const CategoryTitle = styled.div`
    width: 26px;
    height: 15px;
    margin-top: 8px;
    ${
        props => css`
            ${skeletonStyle(props.light)}
        `
    }
`;

/animation/skeleton/style.js (funciona para cualquier container)

import { css, keyframes } from 'styled-components';

const skeletonBackground = (light) => (
    css`
        background: ${ !light
            ? css`linear-gradient(-90deg, #C1C1C1 0%, #F8F8F8 50%, #C1C1C1 100%)`
            : css`linear-gradient(-90deg, #F0F0F0 0%, #F8F8F8 50%, #F0F0F0 100%)`};
            background-size: 400% 400%;
            animation: ${skeletonLoading} 1.2s ease-in-out infinite;
    `
)

const skeletonLoading = keyframes`
    from {
        background-position: 0% 0%;
    }
    to {
        background-position: -135% 0%;
    }
`;

export const skeletonStyle = (light = true) => skeletonBackground(light);

Resultado final:

Dale like si pensaste en meter el <LOADER /> que nos ense帽o @Sparragus en el curso de react.

馃槂鉁

鈥 as铆 va mi proyecto:

Repo de la clase aqu铆

Retocando el dise帽o un poco 馃槂

Pueden usar nprogress bar para hacer un loading muy r谩pido y sencillo.

https://loading.io/ <- Aqu铆 hay un mont贸n de Loadings fabulosos

Cree un componente de Loading que llamo desde el la ternaria en el List Component

import React from 'react'
import { SquareLoading, SquareInside } from './styles'

export const Loading = () => {
  return (
    <SquareLoading>
      <SquareInside />
      <SquareInside />
      <SquareInside />
    </SquareLoading>
  )
}

y los estilos as铆

import styled, { keyframes } from 'styled-components'

const ldsFacebook = keyframes`
    0% {
        top: 6px;
        height: 51px;
    }
    50%, 100% {
        top: 19px;
        height: 26px;
    }
`

export const SquareLoading = styled.div`
    display: block;
    position: relative;
    width: 64px;
    height: 64px;
    margin: 0 auto 5px;
`
export const SquareInside = styled.div`
    display: inline-block;
    position: absolute;
    left: 6px;
    width: 13px;
    background: #222;
    animation: ${ldsFacebook} 1.2s cubic-bezier(0, 0.5, 0.5, 1) infinite;
    &:nth-child(1) {
        left: 6px;
        animation-delay: -0.24s;
    }
    &:nth-child(2) {
        left: 26px;
        animation-delay: -0.12s;
    }
    &:nth-child(3) {
        left: 45px;
        animation-delay: 0;
    }
`

y listo esto lo llamo desde donde quiera y puedo colocar un loading facil

Yo cree este loader con svg y mucha ayuda de este post
Building a pure CSS animated SVG spinner

Aqu铆 tambi茅n les dejo el CodeSandbox donde lo hice: Simple Loader

Dentro del fetch se puede agregar un Catch del error, por si falla la consulta y fijar el valor de Loading a false, esto evitaria que el loading se quede para siempre.

 fetch(url)
      .then(res => {
        return res.json()
      })
      .then(data => {
        setcategories(data)
        setLoading(false)
      }).catch(error=>{
	setLoading(false)
	}

Tambien agregue un spiner y lo incorpore a las animaciones de la siguiente forma:


const loaderKeyframe = keyframes`
{
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
`
const LoaderAnimation = ({
  time = '1s',
  type = 'linear',
  iteration = 'infinite'
} = {}) =>
  css`
    animation: ${time} ${loaderKeyframe} ${type} ${iteration};
  `

export const Loader = styled.div`
  display: flex;
  width: 100%;
  padding: 20px 0;
  align-items: center;
  justify-content: center;
  &:after {
    ${LoaderAnimation};
    content: ' ';
    display: block;
    width: 46px;
    height: 46px;
    margin: 1px;
    border-radius: 50%;
    border: 5px solid #fff;
    border-color: #69dafd transparent #69dafd transparent;
  }
`

As铆 se ver铆a mi loading

Hola excelente curso, gracias!!!. No cabe duda que con hooks se simplifican mucho las cosas. No m谩s conect, mapStateToProps鈥 ni cosas raras 馃槂. Normalmente uso redux-sagas y el setup es una patada en las 馃か

Cargar Categor铆as

la animaci贸n

Este es mi Spinner y aqu铆 encuentras el c贸digo.

Agregue un spinner sencillo

Estos son los estilos

const rotateFrames = keyframes`
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }


export const rotate = () => css`
  animation: 1.5s ${rotateFrames} linear infinite;
`

Y este el componente Spinner

export const Spinner = styled.div`
  ${rotate()}
  border: 2px solid violet;
  border-right: 2px solid transparent;
  border-radius: 50%;
  height: 40px;
  margin-bottom: 10px;
  margin-left: calc(50% - 40px);
  width: 40px;
  z-index: 10;
`

as铆 se ve el mio jeje

Se podria aplicar skeleton para el loading

Usando Styled-components use esta pagina https://loading.io/css/ y lo converti. 馃槃

Para usar la animacion en nuestra lista fija de categorias simplemente debemos importar la funcion fadeIn de animations y usarla cuando se agrega la clase

export const List = styled.ul`
  display: flex;
  overflow: scroll;
  width: 100%;
  &.fixed {
    background: #fff;
    border-radius: 60px;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
    left: 0;
    margin: 0 auto;
    max-width: 400px;
    position: fixed;
    right: 0;
    top: -20px;
    transform: scale(.5);
    z-index: 1;
    ${fadeIn({ time: '1s' })}
  }
`

As铆 esta mi proyecto

`

Para este caso, prefer铆 dar feedback con un icono de loading y rotarlo con una animaci贸n. Dejo mi c贸digo:

import styled, { css, keyframes } from "styled-components";
import { AiOutlineLoading3Quarters } from "react-icons/ai";

...

const rotate = keyframes`
  from {
    transform: rotate(0deg);
  }

  to {
    transform: rotate(360deg);
  }
`;

...

const Loading = styled(AiOutlineLoading3Quarters)`
  color: #000;
  margin: 0 auto;
  animation: ${rotate} 2s linear infinite;
`;

export {
    ...
    Loading,
};

Esta es la animaci贸n para que cargue como Facebook(Skeleton Animation)

const placeHolderShimmer = keyframes`
    0%{
        background-position: -490px 0
    }
    100%{
        background-position: 490px 0
    }
`
export const skeletonAnimation = ({
  time = '1s',
  fill = 'forwards',
  iteration = 'infinite',
  timingFunction = 'linear',
  colorBackground = '#f6f7f8',
  colorAnimation = '#edeef1'
} = {}) =>
  css`
    animation-duration: ${time};
    animation-fill-mode: ${fill};
    animation-iteration-count: ${iteration};
    animation-name: ${placeHolderShimmer};
    animation-timing-function: ${timingFunction};
    background-image: -webkit-gradient(
      linear,
      left center,
      right center,
      from(${colorBackground}),
      color-stop(0.2, ${colorAnimation}),
      color-stop(0.4, ${colorAnimation}),
      to(${colorBackground})
    );
    background-image: -webkit-linear-gradient(
      left,
      ${colorBackground} 0%,
      ${colorAnimation} 20%,
      ${colorBackground} 40%,
      ${colorBackground} 100%
    );
    position: relative;
  `

Hola tengo una pregunta no es mejor que el state del loader empiece en true ya que al cargar la p谩gina como bien se dice carga. Se que funciona de igual manera pero para tener m谩s concordancia a la realidad.

Mi ejemplo de loader lo saque de aca https://skeletonreact.com/?ref=morioh.com&utm_source=morioh.com

import React from 'react'
import ContentLoader from 'react-content-loader'

export const MyLoader = (props) => (
  <ContentLoader
    width={500}
    height={100}
    viewBox="0 0 500 100"
    backgroundColor="#f3f3f3"
    foregroundColor="#ecebeb"
    {...props}
  >
    <circle cx="46" cy="38" r="38" />
    <rect x="34" y="83" rx="5" ry="5" width="25" height="10" />
    <rect x="547" y="222" rx="5" ry="5" width="220" height="10" />
    <rect x="82" y="150" rx="5" ry="5" width="220" height="10" />
    <circle cx="137" cy="38" r="38" />
    <rect x="124" y="83" rx="5" ry="5" width="25" height="10" />
    <circle cx="228" cy="38" r="38" />
    <rect x="215" y="83" rx="5" ry="5" width="25" height="10" />
    <circle cx="320" cy="38" r="38" />
    <rect x="307" y="83" rx="5" ry="5" width="25" height="10" />
    <circle cx="410" cy="38" r="38" />
    <rect x="398" y="83" rx="5" ry="5" width="25" height="10" />
  </ContentLoader>
)

Hola Devs! le agregu茅 un skeleton load a las categorias:

JSX

  const skeletonLoadCategories = () => {
    return [1, 2, 3, 4, 5, 6].map(item => (
      <Item key={item + 'categoriesSkeleton'}>
        <SkeletonItem />
      </Item>
    ))
  }

  const renderList = (fixed) => (
    <List fixed={fixed}>
      {
        loading
          ? skeletonLoadCategories()
          : categories.map(category => <Item key={category.id}><Category {...category} /></Item>)
      }
    </List>
  )

styles.js

export const SkeletonItem = styled.div`
  ${twinkle};
  width: 70px;
  height: 70px;
  border-radius: 50%;
  background: #ddd;
`

Y aqu铆 esta el code de la animaci贸n:

const twinkleKeyFrames = keyframes`
  0% {
    filter: blur(1px);
    opacity: .5;
  }
  50% {
    filter: blur(1px);
    opacity: 1;
  }
  100% {
    filter: blur(1px);
    opacity: .5;
  }
`

export const twinkle = ({ time = '1.5s', type = 'linear' } = {}) => css`animation: ${time} ${twinkleKeyFrames} ${type} infinite;`

Les recomiendo usar este paquete de NPM; react-loader-spinner
est谩n buen铆simos!

Si quieren simular una 鈥淐arga lenta鈥 del sitio web, se puede configurar desde el navegador:

  1. Click derecho -> Inspeccionar
  2. Click en Network / Red
  3. Abrir lista desplegable (Por defecto esta seleccionado No throttling)
  4. Seleccionar Slow 3G o offline

De esta manera el navegador se comportar谩 como si tuviera una conexi贸n lenta (o incluso nula) a internet lo que hara que el fetch tome m谩s tiempo en completarse, o que no se haga y el loading quede activo.

Una forma de hacer el loading, en mi caso utilice como base un loader de aqu铆: https://loading.io/css/
El c贸digo aqu铆

Reto:

Loading/index.js

import React from 'react';

import { ContainerCategorySkeleton, CategoryImage, CategoryTitle } from'./styles';

export const Loading = props => {
    console.log(props)
    return (
        <ContainerCategorySkeleton>
            <CategoryImage light={props.light} />
            <CategoryTitle light={props.light} />
        </ContainerCategorySkeleton>
    )
}

Loading/styles.js

import styled, {css} from 'styled-components';
import { skeletonStyle } from '../../Styles/skeleton';

export const ContainerCategorySkeleton = styled.div`
    display: flex;
    flex-direction: column;
    align-items: center;
`;

export const CategoryImage = styled.div`
    width: 75px;
    height: 75px;
    border-radius: 50%;
    ${
        props => css`
            ${skeletonStyle(props.light)}
        `
    }
`;

export const CategoryTitle = styled.div`
    width: 15px;
    height: 15px;
    margin-top: 8px;
    margin-bottom: 8px;
    border-radius: 50%;
    ${
        props => css`
            ${skeletonStyle(props.light)}
        `
    }
`;

Styles/skeleton.js

import { css, keyframes } from 'styled-components';

const skeletonBackground = (light) => (
    css`
        background: ${ !light
            ? css`linear-gradient(-90deg, #C1C1C1 0%, #F8F8F8 50%, #C1C1C1 100%)`
            : css`linear-gradient(-90deg, #F0F0F0 0%, #F8F8F8 50%, #F0F0F0 100%)`};
            background-size: 400% 400%;
            animation: ${skeletonLoading} 1.2s ease-in-out infinite;
    `
)

const skeletonLoading = keyframes`
    from {
        background-position: 0% 0%;
    }
    to {
        background-position: -135% 0%;
    }
`;

export const skeletonStyle = (light = true) => skeletonBackground(light);

ListOfCategories/index.js

import React, {useState, useEffect} from 'react';

import {List, Item} from './styles';

import {Loading} from '../Loading';
import { Category } from '../Category';

function useCategoriesData() {
    const [categories, setCategories] = useState([]);
    
    const [loading, setLoading] = useState(false);
    
    //Show Categories fetching the data
    useEffect(function(){
        setLoading(true)
        window.fetch('https://petgram-server-alejandroverita-alejandroverita.vercel.app/categories')
            .then(res => res.json())
            .then(data => {
                setCategories(data);
                setLoading(false);
        })
    }, [])

    return { categories, loading }
}

export const ListOfCategories = () => {
    const {categories, loading } = useCategoriesData();
    
    const [showFixed, setShowFixed] = useState(false);

    //Show categories when scroll is higher 200px
    useEffect(()=>{
        const onScroll = e => {
            const newShowFixed = window.scrollY > 200
            showFixed !== newShowFixed && setShowFixed(newShowFixed)
        }

        document.addEventListener('scroll', onScroll)

        //Avoid memoryLeak
        return () => document.removeEventListener('scroll', onScroll)
    }, [showFixed])

    const renderList = (fixed) => (
        <List fixed={fixed}>
            {
                loading 
                ? 
                <Item key = 'loading'>
                    <Loading />
                </Item>
                : 
                categories.map(category => 
                    <Item key = {category.id}>
                        <Category {...category} />
                    </Item>
                )
            }
        </List>
    )

    
    return (
        <>
           
            {renderList()}
            {showFixed && renderList(true)}
        </>
    )
}

Alguien m谩s redujo el c贸digo creando otro custom hook para el scroll, se ve mas limpio y para el loader use un icon y le di un rotate infinito ^^

Estoy realizando todo el proyecto con typescript, les dejo una peque帽a parte del c贸digo, adicional los spinners los realice con material-ui

import { CircularProgress } from "@mui/material";
import React, { FC, useState, useEffect } from "react";
import { Category } from "../Category";

import { List, Item } from "./styles";
const useCategoriesDate = () => {
  const [loading, setLoading] = useState(true);
  const [categories, setCategories] = useState<categories[]>([]);
  useEffect(() => {
    const query = async () => {
      try {
        const respuesta = await fetch("https://petgram-mikex.vercel.app/categories");
        setLoading(false);
        return setCategories(await respuesta.json());
      } catch (error) {
        console.error(error);
      }
    };
    query();
  }, []);

  return { categories, loading };
};

interface categories {
  id: number;
  name: string;
  emoji: string;
  cover: string;
  path: string;
}

export const ListCategories: FC = () => {
  const { categories, loading } = useCategoriesDate();
  const [showFixed, setShowFixed] = useState(true);

  useEffect(() => {
    const onScroll = (e: Event) => {
      const newShowFixed = window.scrollY < 200;
      showFixed !== newShowFixed && setShowFixed(newShowFixed);
    };

    document.addEventListener("scroll", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
    };
  }, [showFixed]);

  const renderList = (boolVar: boolean) => (
    <List className={(boolVar && ((showFixed && "fixed none") || "fixed")) || ""}>
      {(loading &&
        [1, 2, 3, 4, 5].map((i) => (
          <CircularProgress 
            style={{ width: "65px", height: "65px", margin: ".8em" }} 
            variant="indeterminate" 
            color="secondary" key={i} />
        ))) ||
        categories.map((category) => (
          <Item key={category.id}>
            <Category {...category} />
          </Item>
        ))}
    </List>
  );

  return (
    <React.Fragment>
      {renderList(false)}
      {renderList(true)}
    </React.Fragment>
  );
};

Hola Miguel! Es cierto muy 煤til utilizar las props de styled-component, pero si necesitamos una animaci贸n de entrada y otra se salida, no ser铆a mejor usar clases de css? Muchas gracias!

Aqu铆 el m铆o, le puse un retardo para que se notara.

Genial

Est谩 genial, resulta m谩s pr谩ctico que con clases usar s贸lo funciones y hooks

Super Claseee!!!

Bastante contento con el curso hasta ahora.

Yo le he puesto un peque帽o Spinner, cogiendo el c贸digo desde esta p谩gina https://loading.io/css/

Aqu铆 el resultado por ahora:

Excelente el ejemplo de los cutom hooks , ya queda un poco mas claro cuando poder usarlos

Para agregar nprogress yo cree otro useEffect donde pregunto por el estado y uso nprogress.start() y done()
como no se trata de renderizar un componente como tal sino de usar la libreria lo hice asi.

Si lo estoy usando de manera incorrecta por favor alguien que me aviseee.

Mi c贸digo con comentarios:

import React, { Fragment, useEffect, useState } from 'react'
import { Category } from '../Category'

import { List, Item } from './styles'

// Costum Hook
function useCategoriesData () {
  // Creando estados iniciales.
  const [categories, setCategories] = useState([])
  const [loading, setLoading] = useState(false)

  useEffect(function () {
    setLoading(true)
    window.fetch('http://localhost:3000/categories')
      .then(res => res.json())
      .then(response => {
        setCategories(response)
        setLoading(false)
      })
  }, [])


  return { categories, loading }
}

export const ListOfCategories = () => {

  // Obtener los valores de los estados en en el custom hook
  const { categories, loading } = useCategoriesData()
  
  const [showFixed, setShowFixed] = useState(false)


  useEffect(function () {
    const onScroll = e => {
      const newShowFixed = window.scrollY > 200
      showFixed !== newShowFixed && setShowFixed(newShowFixed)
    }

    document.addEventListener('scroll', onScroll)

    return () => document.removeEventListener('scroll', onScroll)
  }, [showFixed])

  const renderList = (fixed) => (
    <List fixed={fixed}>
      {
        loading
          ? <Item key='loading'><Category /></Item>
          : categories.map(category => <Item key={category.id}><Category {...category} /></Item>)
      }
    </List>
  )

  return (
    <Fragment>
      {renderList()}
      {showFixed && renderList(true)}
    </Fragment>
  )
}

Yo use la libreria skeleton
react-loading-skeleton

Este es mi c贸digo con un icono simple de react-loading
Tienen que descargar la libreria con npm install react-loading para que les funciones en . Tambi茅n pueden cambiar el tipo de icono y su color!

import React, { Fragment, useEffect, useState } from 'react'
import { Category } from '../Category'
import { List, Items } from './styles'
import ReactLoading from 'react-loading';


const CategoriesData = () => {
  const [categories, setCategories] = useState([])
  const [loading, setLoading] = useState(false)

  useEffect(function () {
    setLoading(true)
    window.fetch('https://petgram-server-rdct.vercel.app/categories')
      .then(res => res.json())
      .then(response => {
        setCategories(response)
        setLoading(false)
      })
      
  }, [])
  return {categories, loading }
}




export const ListOfCategories = () => {
 
  const [showFixed, setShowFixed] = useState(false)
  const {categories, loading} = CategoriesData()
 

  useEffect(() => {
    const onScroll = e => {
      const newShowFixed = window.scrollY > 200
      showFixed !== newShowFixed && setShowFixed(newShowFixed)
    }

    document.addEventListener('scroll', onScroll)

    return () => document.removeEventListener('scroll', onScroll)
  }, [showFixed])



  const renderList = (fixed) => (
    <List fixed= {fixed}>
      {
      loading ? (<ReactLoading type= 'spokes' color= "#000000"/>) :
       categories.map(category => <Items key={category.id}><Category {...category} /></Items>)
      }
    </List>
  )



  return (
    <>
    
    {renderList()}
    {showFixed && renderList(true)}
   
    </>
  )
}

Utilice la pagina https://loading.io/css/
modifique un poro el c贸digo y lo implemente en un componente

import React from 'react';
import { Div } from './styles';

export const LoadingSpinner = () => (
	<Div>
		<div> </div>
		<div> </div>
		<div> </div>
	</Div>
);
import styled, { keyframes } from 'styled-components';

const loadingKeyFrames = keyframes`
	from {
	    transform: rotate(0deg);
	  }
	  to {
	    transform: rotate(360deg);
	  }
`;

export const Div = styled.div`

	display: inline-block;
  position: relative;
  width: 80px;
  height: 80px;
	animation: ${loadingKeyFrames} 2s linear infinite;

	& div {
		box-sizing: border-box;
	  display: block;
	  position: absolute;
	  width: 64px;
	  height: 64px;
	  margin: 8px;
	  border: 8px solid #f3a474;
	  border-radius: 50%;
	  animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
	  border-color: #f3a474 transparent transparent transparent;
	}

	& div:nth-child(1) {
		animation: ${loadingKeyFrames} 2.1s linear infinite;
	}

	& div:nth-child(2) {
		animation: ${loadingKeyFrames} 2.2s linear infinite;
	}

	& div:nth-child(3) {
		animation: ${loadingKeyFrames} 2.3s linear infinite;
	}
`;

Mi soluci贸n usando https://tobiasahlin.com/spinkit/

src\components\Loader\index.js

import React from 'react'
import { Spinner } from './Styles'

export const Loader = () => {
  return (
    <Spinner />
  )
}

src\components\Loader\Styles.js

import styled, { keyframes } from 'styled-components'

const skRotateplane = keyframes`
  0% { -webkit-transform: perspective(120px) }
  50% { -webkit-transform: perspective(120px) rotateY(180deg) }
  100% { -webkit-transform: perspective(120px) rotateY(180deg)  rotateX(180deg) }
`

export const Spinner = styled.div`
  width: 40px;
  height: 40px;
  background-color: #333;
  margin: 100px auto;
  animation: ${skRotateplane} 1.2s infinite ease-in-out;
`

Loading con skeleton

Bueno lo que hice, para resolver esto es crear en la carpeta styles un archivo CategorySkeleton

Mi idea es, mas adelante cuando se haga lo mismo con el componente de listOfPhotoCategory retocarlo y que sea cuadrado y asi tener un mismo archivo

import styled, { css, keyframes } from 'styled-components'

const skeletonBackground = () => (
  css`animation: ${skeletonLoading} 1.6s ease-in-out infinite;`
)

const skeletonLoading = keyframes`
  from {
    background-position: 0% 0%;
    background: linear-gradient(-90deg, #C1C1C1 0%, #F8F8F8 50%, #C1C1C1 100%);
    background-size: 400% 400%;
  }
  to {
    background-position: -135% 0%;
    background: linear-gradient(-90deg, #F0F0F0 0%, #F8F8F8 50%, #F0F0F0 100%);
    background-size: 400% 400%;
  }
`

export const ListSkeleton = styled.div`
  margin-bottom: 20px;
`
export const ListSkeletonPhoto = styled.div`
  ${skeletonBackground()}
  background: #909090;
  height: 10vh;
  width: 5vw;
  border-radius: 80%;
  margin-bottom: 10px;
`
export const ListSkeletonBody = styled.div`
  ${skeletonBackground()}
  background: #909090;
  height: 20px;
  width: 30px;
  margin: auto;
`

Index>Category

 const renderList = (fixed) => (
    <List fixed={fixed}>
      {
        loading
          ? [1, 2, 3, 4, 5, 6].map((key) => {
            return (
              <Item key={key}>
                <ListSkeleton>
                  <ListSkeletonPhoto />
                  <ListSkeletonBody />
                </ListSkeleton>
              </Item>
            )
          })
          : (
            categories.map((category, key) =>
              <Item key={key}>
                <Category
                  {...category}
                />
              </Item>
            )
          )
      }
    </List>
  )

Es fascinante!!!

Yo muestro contenedores vacios con shadows internos y cuando cargan las im谩genes pues se muestran con un fade in

yo use react-loader-spinner, este es mi resultado:

Osea que as铆 de sencillo es crear tu propio hook. Solo es una funci贸n que devuelve un valor y ya xD Cre铆 que iba a ser mucho m谩s dif铆cil.

Excelente el conjunto de buenas pr谩cticas, creo que no voy tan perdido

Para el loading use el componente 鈥渞eact-loader-spinner鈥 es muy facil de instalar y tiene muchas opciones de animacion para usar.

https://www.npmjs.com/package/react-loader-spinner

b

Hola, les dejo mi repositorio de este proyecto, pero hecho en Nextjs:

https://github.com/danyel117/petgram-platzi/tree/hookdatacategorias

us茅 el c贸digo de la animaci贸n para cuando la categor铆a no ha cargado de iLuisCastillo

Muy bueno 馃槂