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

Curso de React Avanzado

Curso de React Avanzado

Miguel Ángel Durán

Miguel Ángel Durán

Controlar estado de carga y error al registrar un usuario

36/50
Recursos

Aportes 29

Preguntas 1

Ordenar por:

Los aportes, preguntas y respuestas son vitales para aprender en comunidad. Regístrate o inicia sesión para participar.

La cantidad de comentarios ha decrecido conforme se va avanzado, así que dejaré el mio. El curso está muy bueno, tal vez algo “espeso” por la cantidad de conocimientos previos que hay que tener para poder sacar el máximo provecho. En todo caso hace honor a su título de “avanzado”.

Asi quedo mi implementación:

Manejo de estado de acrga con hooks

import React from 'react'
import Context from '../Context'
import { UserForm } from '../components/UserForm'
import { useRegisterMutation } from '../components/hooks/useRegisterMutation'

export const NotRegisteredUser = () => (
  <Context.Consumer>
    {
      ({ isAuth, activateAuth }) => {
        return (
          <>
            <Registro activateAuth={activateAuth} />
            <UserForm activateAuth={activateAuth} title='Iniciar sesión' />
          </>
        )
      }
    }
  </Context.Consumer>
)

const Registro = ({ activateAuth }) => {
  const { register, loading, error } = useRegisterMutation()
  const onSubmit = ({ email, password }) => {
    const input = { email, password }
    const variables = { input }
    register({ variables }).then(res => {
      activateAuth()
    })
  }
  const errorMsg = error && 'El usuario ya existe o hay algún problema.'
  return <UserForm disabled={loading} error={errorMsg} onSubmit={onSubmit} title='Registrarse' />
}

Mi progreso /o/

En esta clase he tenido 2 grandes problemas (en mi caso), se los comparto por si se los encuentran.

El primero era con la la mutación de registro del usuario, cuando se ejecutaba el onSubmit y hacia la mutación me daba un error 404 indicando que los valores del input(email, password) no eran un string lo cual para mi era muy raro porque sí eran string pero los estaba utilizando mal, la solución era acceder al su valor de cada propiedad:

/* UseForm - index.js */

El segundo problema el hook de useRegisterMutation en la carpeta ‘container’, no podía acceder al “error” ni al “loading” de la mutación. Esto era debido a que estaba obteniendo erroneamente estos valores:

Just in case dejo mi codigo de NotRegisteredUser:

Este fue el resultado de esta clase en mi aplicación 😃

En mi caso personal el registro fallaba si no hacía un catch del error de la promesa, ésto porque mi app no sabía cómo manejar el error, es muy diferente el error de la promesa que el error de la render prop y tenemos que cachar los 2:
.
Dejo el código:
.

<RegisterMutation>
                {
                  (register, { loading, data, error }) => {
                    const onSubmit = ({ email, password }) => {
                      const input = { email, password };
                      const variables = { input };
                      register({ variables })
                        .then(activateAuth)
                        .catch((error) => { console.log(error.graphQLErrors[0].message) ; });
                    };

                    const errorMsg = error && error.graphQLErrors[0].message;
                    console.log(errorMsg);
                    return <UserForm disabled={loading} error={errorMsg} onSubmit={onSubmit} title="Registrarse" />;
                  }
                }
</RegisterMutation>

👏👏👏
Es buen profesor porque:
• Resume la clase anterior y avisa lo que hará en esta
• Luego enseña el código
• Al final recapitula a modo de resumen.
Con esto entiendes lo que haces, recibes claves importantes e incita a pensar

Bueno para aquellos que estan usando hooks de apollo
Les dejo como lo resolvi, seguro les es de utilidad

NotResgistrerUser.js

import React, { useState } from 'react'
import { UserForm } from '../components/UserForm/Index'

export const NotRegisteredUser = () => {
  const [view, setView] = useState(false)
  return (
    <>
      {
        view === true
          ? <UserForm title='registrarse' />
          : <UserForm title='Iniciar sesion' setView={setView} />
      }
    </>
  )
}

UserForm

import React from 'react'
import { useStateValue } from '../../Context'
import { useInputValue } from '../../hooks/useInputValue'
import AssetImage from '../../../assets/pets.png'
import {
  Form,
  Image,
  Container,
  Button,
  Input,
  Span,
  Spinner
} from './styles'
import { signup } from '../../graphql/RegisterMutation'
import { ToastsStore } from 'react-toasts'


export const UserForm = ({title, setView }) => {
  const email = useInputValue('')
  const password = useInputValue('')
  const { registerMutation, data, loading, error } = signup(email.value, password.value)

  const [{ }, dispatch] = useStateValue()

  const HandleOnSubmit = (event) => {
    event.preventDefault()
    if (title === 'Iniciar sesion') {
      dispatch({
        type: 'LOGIN'
      })
    } else {
      registerMutation()
    }
  }
  if (data) {
    console.log('capture la data')
    dispatch({
      type: 'LOGIN'
    })
  }
  if (error) {
    ToastsStore.error('unexpected error')
  }

  return (
    <Container>
      <Image src={AssetImage} />
      <Span>
        Inicia sesion en Petgram y descubre el mundo de las mascotas
      </Span>
      <Form onSubmit={HandleOnSubmit}>
        <Input
          placeholder='email'
          {...email}
        />
        <Input
          placeholder='password'
          type='password'
          {...password}
        /><br />
        {
          loading
            ? <Spinner />
            : <Button> {title}</Button>
        }
      </Form>
      {
        title === 'Iniciar sesion' &&
          <span>
            No tienes una cuenta? <a onClick={() => setView(true)}>Registrate</a>
          </span>
      }
    </Container>
  )
}

registrer mutations

import { useMutation } from '@apollo/client'
import { gql } from 'apollo-boost'

const REGISTER = gql`
  mutation signup($input:UserCredentials!) {
    signup (input: $input)
  }
`

export const signup = (email, password) => {
  const [registerMutation, { data, loading, error }] = useMutation(REGISTER, { variables: { input: { email, password } } })

  return { registerMutation, data, loading, error }
}

Un estilo para el error un poco mas visible:

export const Error = styled.span`
    display: block;
    font-size: 14px;
    color: red;
    text-align: center;
    padding: 8px 0px;
    margin: 10px 0px 10px;
    width: 100%;
    border:dotted 3px #f51165;
    -moz-border-radius-topleft: 8px;
    -moz-border-radius-topright:8px;
    -moz-border-radius-bottomleft:8px;
    -moz-border-radius-bottomright:8px;
    -webkit-border-top-left-radius:8px;
    -webkit-border-top-right-radius:8px;
    -webkit-border-bottom-left-radius:8px;
    -webkit-border-bottom-right-radius:8px;
    border-top-left-radius:8px;
    border-top-right-radius:8px;
    border-bottom-left-radius:8px;
    border-bottom-right-radius:8px;
    background-color: rgba(215, 44, 44, 0.3);
    background: rgba(215, 44, 44, 0.3);    
`

En mi caso estoy usando hooks y la función para registrase me quedó así:

  const onRegister = async ({ email, password }) => {
    setLoading(true)
    const input = { email, password }
    try {
      const data = await register({ variables: { input } })
      setIsLogged(!!data)
    } catch {
      setRegisterError('El usuario ya existe o hay algún problema')
    } finally {
      setLoading(false)
    }
  }

el loading y el registerError son useState hooks del componente NotRegisterUser

Les dejo un Spinner para que coloquen debajo del formulario, cuando loading sea true…

export const Spinner = styled.div`
   @keyframes spin {
     0% {
       transform: rotate(0deg);
     }
   
     100% {
       transform: rotate(360deg);
     }
    
  }
   
border: 4px solid rgba(0, 0, 0, 0.1);
width: 36px;
height: 36px;
 border-radius: 50%;
 border-left-color: #09f;
  margin:auto;
 animation: spin 1s ease infinite;
`

UserForm/index.js

import React from 'react';
import { useInputValue } from '../../Hooks/useInputValue';

import { Form, Input, Button, Title, Error } from './styles';

export const UserForm = ({onSubmit, title, error, disabled}) => {

    const email = useInputValue('');
    const password = useInputValue('');

    const handleSubmit = (e) => {
        e.preventDefault();
        onSubmit({
            email: email.value, 
            password: password.value
        })
    }
    
    return ( 
        <>
            <Form disabled={disabled} onSubmit={handleSubmit}>
                <Title>{title}</Title>
                <Input disabled={disabled} placeholder='Email' {...email} />
                <Input disabled={disabled} placeholder='Password' type='Password' {...password} />
                <Button disabled={disabled}>{title}</Button>
            </Form>
            {error && <Error>{error}</Error>}
        </>
    )
}

UserForm/styled.js

import styled from 'styled-components'

export const Form = styled.form ` 
    padding: 16px 0;
`

export const Input = styled.input ` 
    border: 1px solid #ccc;
    border-radius: 3px;
    margin-bottom: 8px;
    padding: 8px 4px;
    display: block;
    width: 100%;
    &[disabled] {
        opacity: 0.3;
    }
`

export const Button = styled.button` 
    background: #8d00ff;
    border-radius: 3px;
    color: #fff;
    height: 32px;
    display: block;
    width: 100%;
    text-align: center;
    &[disabled] {
        opacity: 0.3;
    }
`

export const Title = styled.h2`
  color: #8d00ff;
  text-align: center;
  font-size: 16px;
  font-weight: 500;
  padding: 8px 0;
` 

export const Error = styled.span ` 
    font-size: 14px;
    color: red;
`

Pages/NotRegisteredUser.js

import React, {useContext} from 'react';
import {AppContext} from '../Context/AppContext';
import { UserForm } from '../Components/UserForm';
import { RegisterMutation } from '../Container/RegisterMutation';

export const NotRegisteredUser = () => {
    const { activateAuth } = useContext(AppContext)
  return (
    <>
    <RegisterMutation>
      {
        (register, {data,loading, error})=> {
          const onSubmit = ({email, password}) =>{
            const input = {email, password} 
            const variables = {input} 
            register({variables}).then(activateAuth)
          }

          const errorMsg = error && 'User already exists.'
          
          return <UserForm disabled={loading} error={errorMsg} title='Log In' onSubmit={onSubmit}/>
        }
      }
    </RegisterMutation>
    <UserForm title='Sign In' onSubmit={activateAuth}/>
    </>
  )
}

Ya segunda vez que veo el curso y sigo asombrandome de los conocimientos del profe… con fe y algún día llegaremos a ese nivel

Cambie render props por hooks

import { useMutation } from '@apollo/react-hooks';
import gql from 'graphql-tag';

const REGISTER = gql`
mutation signup($input: UserCredentials!) {
    signup (input: $input)
  }
`;

const useRegisterUser = (email, password) => {
  const [
    register, { data, loading, error },
  ] = useMutation(REGISTER, { variables: { input: { email, password } } });
  return {
    register, data, loading, error,
  };
};

export default useRegisterUser;

03/06/2022 (hook, react-router-dom, @apolo/client)
src/hooks/userRegisterMutation.js:

import { gql, useMutation } from "@apollo/client";

const REGISTER = gql`
    mutation signup($input: UserCredentials!){
        signup(input: $input)
    }
`

export const useRegisterMutation = ()=> {
    const [registerMutation, { loading, error }] = useMutation(REGISTER)

    return { registerMutation, loading, error }
}

src/pages/NotRegisteredUser.js:

import React from 'react';
import Context from '../Context';
import { UserForm } from '../components/UserForm'
import { useRegisterMutation } from '../hooks/useRegisterMutation';

export const NotRegisteredUser = ()=> {
    const { registerMutation, loading, error } = useRegisterMutation()

    return (
        <Context.Consumer>
            {
                ({ activateAuth })=> {
                    const onSubmit = ({ email, password }) => {
                        const input = { email, password }
                        const variables = { input }
                        registerMutation({ variables })
                            .then(activateAuth)
                    }

                    const errorMsg = error && 'El usuario ya existe.'

                    return  <>
                        <UserForm 
                            disabled={loading} 
                            error={errorMsg} 
                            title='Registrarse' 
                            onSubmit={onSubmit} 
                        />
                        <UserForm 
                            title='Iniciar sesion' 
                            onSubmit={activateAuth} 
                        />
                    </>
                }
            }
        </Context.Consumer>
    )
}

src/components/UserForm/index.js:

import React from 'react';
import { useInputValue } from '../../hooks/useInputValue';
import { Title, Form, Input, Button, Error } from './styles'

export const UserForm = ({ onSubmit, title, disabled, error })=> {
    const email = useInputValue('')
    const password = useInputValue('')

    const handleSubmit = (event)=> {
        event.preventDefault()
        onSubmit({
            email: email.value,
            password: password.value
        })
    }

    return (
        <>
            <Form disabled={disabled} onSubmit={handleSubmit}>
                <Title>{title}</Title>
                <Input 
                    disabled={disabled} 
                    placeholder='email' {...email} 
                />
                <Input 
                    disabled={disabled} 
                    placeholder='password' {...password} 
                />
                <Button disabled={disabled} >{title}</Button>
                {error && <Error>{error}</Error>}
            </Form>
        </>
    )
}

src/components/UserForm/styles.js:

import styled from 'styled-components'

export const Form = styled.form`
    padding: 16px 0;
`

export const Input = styled.input`
    border: 1px solid #ccc;
    border-radius: 3px;
    margin-bottom: 8px;
    padding: 8px 4px;
    display: block;
    width: 100%; 
    &[disabled] {
        opacity: .3; 
    }
`

export const Button = styled.button`
    background: #8d00ff;
    border-radius: 3px;
    color: #fff;
    height: 32px;
    display: block;
    width: 100%;
    text-align: center;
    &[disabled] {
        opacity: .3; 
    }
`

export const Title = styled.h2`
    font-size: 16px;
    font-weight: 500;
    padding: 8px 0;
`
export const Error = styled.span`
    color: red; 
    font-size: 14px;
`

El loader que utilicé lo saqué de https://loading.io/css/ y lo modifiqué para que pudiera estar acorde a mi componente. Lo comparto por si alguien le gustaría probarlo.
.
/components/Loading/index.js

import { Loader } from './styles'

export const Loading = () => (
  <Loader>
    <div />
    <div />
    <div />
  </Loader>
)

/components/Loading/styles.js

import styled from 'styled-components'
import { loading } from '../../styles/animation'

export const Loader = styled.div`
  display: inline-block;
  position: relative;
  width: 56px;
  height: 40px;

  & div {
  display: inline-block;
  position: absolute;
  width: 12px;
  background: #9e3840;
  ${loading('1.2s', 'cubic-bezier(0, 0.5, 0.5, 1)', 'infinite')}
  }

  & div:nth-child(1) {
  left: 0;
  animation-delay: -0.24s;
  }

  & div:nth-child(2) {
  left: 22px;
  animation-delay: -0.12s;
  }
  & div:nth-child(3) {
  right: 0;
  animation-delay: 0;
  }
`

/styles/animation.js

const loaderKeyframe = keyframes`
  0% {
    top: 2px;
    height: 38px;
  }
  50%, 100% {
    top: 14px;
    height: 19px;
  }
`
export const loading = ({ time = '1s', type = 'cubic-bezier(0, 0.5, 0.5, 1)', animationIterationCount = 'infinite' } = {}) =>
  css`animation: ${time} ${loaderKeyframe} ${type} ${animationIterationCount};`

Hasta el momento, este curso ha resuelto la mayoría de las dudas que me surgieron desarrollando una app real en React Native, muchas gracias.

Si quieren mostrar alertas muy cool en sus aplicaciones de React les recomiendo react-toastify.

https://github.com/fkhadra/react-toastify

Muy buenas validaciones en componentes, aplicando todo el power de react!!!

Debo tener algún error que no encuentro por que el estado disabled del form no se me está haciendo evidente a traves del css

Hola! Me pareció más intuitivo guardar el estado de Error y Loading en objeto de Status, para luego enviarlo por props. Esta manera manera me parece más semántica a la hora de interpretar o saber de qué depende el atributo disabled de los inputs, form y button.

<RegistrarseMutation>
              {(register, { data, loading, error }) => {
                const onSubmit = ({ email, password }) => {
                  const input = { email, password }
                  const variables = { input }
                  register({ variables }).then(activateAuth)
                }

                const ErrorMsg = error && "El usuario ya existe."
                const status = { ErrorMsg, loading }

                return (
                  <UserForm
                    {...status}
                    onSubmit={onSubmit}
                    title="Registrarse"
                  />
                )
              }}
            </RegistrarseMutation>
<Form disabled={loading} action="" onSubmit={handleSubmit}>
        <Title>{title}</Title>
        <Input
          disabled={loading}
          placeholder="Email"
          value={email.value}
          onChange={email.onChange}
        />
        <Input
          disabled={loading}
          type="password"
          placeholder="Password"
          value={password.value}
          onChange={password.onChange}
        />
        <Button disabled={loading}>{title}</Button>
        {ErrorMsg && <Error>{ErrorMsg}</Error>}
      </Form>

Saludos!

Va quedando genial!

Acabo el curso y salgo directo a ponerlo en práctica y comenzar a aprender GraphQL. Qué poderes los que otorga GraphQL con Apollo

Genial como el profesor complementa tantos conceptos y funcionalidades en un solo componente/page

n

Hola, les dejo el avance de mi repositorio, siguiendo este proyecto en Nextjs:

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

genial!!