隆Bienvenida! Este es un curso especial de React Hooks

1

驴Qu茅 aprender谩s en el Curso Profesional de React Hooks?

2

驴Qu茅 son los React Hooks y c贸mo cambian el desarrollo con React?

Introducci贸n a React Hooks

3

useState: estado en componentes creados como funciones

4

useEffect: olvida el ciclo de vida, ahora piensa en efectos

5

useContext: la fusi贸n de React Hooks y React Context

6

useReducer: como useState, pero m谩s escalable

7

驴Qu茅 es memoization? Programaci贸n funcional en JavaScript

8

useMemo: evita c谩lculos innecesarios en componentes

9

useRef: manejo profesional de inputs y formularios

10

useCallback: evita c谩lculos innecesarios en funciones

11

Optimizaci贸n de componentes en React con React.memo

12

Custom hooks: abstracci贸n en la l贸gica de tus componentes

13

Third Party Custom Hooks de Redux y React Router

Configura un entorno de desarrollo profesional

14

Proyecto: an谩lisis y retos de Platzi Conf Store

15

Git Hooks con Husky

16

Instalaci贸n de Webpack y Babel: presets, plugins y loaders

17

Configuraci贸n de Webpack 5 y webpack-dev-server

18

Configuraci贸n de Webpack 5 con loaders y estilos

19

Loaders de Webpack para Preprocesadores CSS

20

Flujo de desarrollo seguro y consistente con ESLint y Prettier

Estructura y creaci贸n de componentes para Platzi Conf Store

21

Arquitectura de vistas y componentes con React Router DOM

22

Maquetaci贸n y estilos del home

23

Maquetaci贸n y estilos de la lista de productos

24

Maquetaci贸n y estilos del formulario de checkout

25

Maquetaci贸n y estilos de la informaci贸n del usuario

26

Maquetaci贸n y estilos del flujo de pago

27

Integraci贸n de 铆conos y conexi贸n con React Router

Integraci贸n de React Hooks en Platzi Conf Merch

28

Creando nuestro primer custom hook

29

Implementando useContext en Platzi Conf Merch

30

useContext en la p谩gina de checkout

31

useRef en la p谩gina de checkout

32

Integrando third party custom hooks en Platzi Conf Merch

Configura mapas y pagos con PayPal y Google Maps

33

Paso a paso para conectar tu aplicaci贸n con la API de PayPal

34

Integraci贸n de pagos con la API de PayPal

35

Completando la integraci贸n de pagos con la API de PayPal

36

Paso a paso para conectar tu aplicaci贸n con la API de Google Maps

37

Integraci贸n de Google Maps en el mapa de checkout

38

Creando un Custom Hook para Google Maps

Estrategias de deployment profesional

39

Continuous integration y continuous delivery con GitHub Actions

40

Compra del dominio y despliega con Cloudflare

Optimizaci贸n de aplicaciones web con React

41

Integraci贸n de React Helmet para mejorar el SEO con meta etiquetas

42

An谩lisis de performance con Google Lighthouse

43

Convierte tu aplicaci贸n de React en PWA

Bonus: trabaja con Strapi CMS para crear tu propia API

44

Crea una API con Strapi CMS y cons煤mela con React.js

驴Qu茅 sigue en tu carrera profesional?

45

Pr贸ximos pasos para especializarte en frontend

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:

5D
16H
41M
12S

useContext en la p谩gina de checkout

30/45
Recursos

Aportes 30

Preguntas 8

Ordenar por:

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

o inicia sesi贸n.

A este punto de la aplicaci贸n creo que hay un bug, cuando se a帽ade m谩s elementos del mismo ID (m谩s de una camiseta, por ejemplo) y quiere borrar una camisa (pero no todas), se eliminan todas las camisas al tener el mismo id

la logica del titulo asi es mas corta:
<h3>{cart.length > 0 ? 鈥極rders list鈥 : 鈥楴o orders鈥檥</h3>
no hace falta repetir tantos h3

隆Hola!, 驴c贸mo est谩n?

Ya saben que debemos siempre estar pendientes de qu茅 es lo que estamos aprendiendo y de mejorarlo en lo que podamos d谩ndole nuestro toque personal, en este caso ya otros estudiantes expusieron el bug que hab铆a al agregar m煤ltiples camisetas en la aplicaci贸n y una forma de darle un arreglo r谩pido, ahora aqu铆 quiero mostrarles algo que me pareci贸 raro que se le pas贸 a Oscar.

En vez de mapear directamente con los elementos que ya ten铆amos all铆, lo que hice fue crear un nuevo componente llamado CheckoutItem al cual se le pasa el producto m谩s el handleRemove, he aqu铆 como est谩 implementado en el return de Checkout.jsx :

 return (
    <div className="Checkout">
      <div className="Checkout-content">
        {cart.length > 0 ? <h3>Lista de pedidos</h3> : <h3>Sin pedidos...</h3>}

        {cart.map((item) => (
          <CheckoutItem product={item} handleRemove={handleRemove(item)} />
        ))}
      </div>

      {cart.length > 0 && (
        <div className="Checkout-sidebar">
          <h3>Precio Total: ${handleSumTotal()}</h3>
          <Link to="/checkout/information">
            <button type="button">Continuar pedido</button>
          </Link>
        </div>
      )}
    </div>
  );

y he aqu铆 como est谩 creado el componente CheckoutItem.jsx:

import React from "react";

import "../styles/components/Checkout.css";

const CheckoutItem = ({ product, handleRemove }) => {
  return (
    <div className="Checkout-item">
      <div className="Checkout-element">
        <h4>{product.title}</h4>
        <span>${product.price}</span>
      </div>
      <button type="button" onClick={handleRemove}>
        <i className="fas fa-trash-alt" />
      </button>
    </div>
  );
};

export default CheckoutItem;

Nuevamente podemos usar useMemo para memorizar el resultado, esto por si de pronto el componente se renderea sin que cambie carrito

Para solucionar el problema al agregar al carrito un mismo producto realice lo siguiente:

Modifique la funcion de AddtoCart para que agregue una cantidad (qty) a los productos

const addToCart = payload => {
        const cartList = state.cart;
        let newCartList = cartList;

        const index = cartList.findIndex(item => item.id === payload.id);

        if (index >= 0) {
            newCartList[index] = {
                ...newCartList[index],
                qty: newCartList[index].qty + 1
            }
        } else {
            payload.qty = 1;
            newCartList = [...cartList, payload]
        }

        setState({
            ...state,
            cart: newCartList
        });
    }

en el header actualice la cantidad total

 const reducer = (acumulador, currentValue) => acumulador + currentValue.qty;
 const totalQty = cart.reduce(reducer, 0)

 <div className='Header-alert'>  {totalQty}  </div>

en el checkout modifique el precio total del carrito, tambien muestro el precio unitario y por producto



const Checkout = () => {
  const { state: { cart }, removeFromCart } = useContext(AppContext);

  const handleRemote = product => {
    removeFromCart(product)
  }

  const handleSumtotal = () => {
    const reducer = (accumulator, currentValue) => accumulator + (currentValue.price * currentValue.qty);
    const sum = cart.reduce(reducer, 0);
    return sum;
  }

  return (
    <div className="Checkout">
      <div className="Checkout-content">
        <h3>Lista de Pedidos:</h3>
        {cart.length > 0
          ? <>
            {cart.map(product => (
              <div key={product.id} className="Checkout-item">
                <div className="Checkout-element">
                  <h4>{product.title}</h4>
                  <span>Cant. {product.qty}</span>
                  <span>$ {product.price}</span>
                  <span>Total: $ {product.price * product.qty}</span>
                </div>
                <button
                  onClick={() => handleRemote(product)}
                  type="button">
                  <i className="fas fa-trash-alt" title="Eliminar" />
                </button>
              </div>
            ))}
          </>
          : <p>No hay Articulos en el carrito</p>}

      </div>
      <div className="Checkout-sidebar">
        <h3>Precio Total: {handleSumtotal()}</h3>
        <Link to='/checkout/information'>
          <button type="button">Continuar pedido</button>
        </Link>
      </div>
    </div>
  );
}

finalmente modifique la funcion removeFromCart para que elimine la cantidad y no el producto completo

const removeFromCart = payload => {
        const cartList = state.cart;
        let newCartList = cartList;

        const index = cartList.findIndex(item => item.id === payload.id);
        newCartList[index] = {
            ...newCartList[index],
            qty: newCartList[index].qty - 1
        }

        const updatedList = newCartList.filter(item => item.qty > 0);

        setState({
            ...state,
            cart: updatedList,
        });
    }

Para los que no entiendan la funcion reduce 鈥

Array.prototype.reduce()

The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in single output value.

const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;

// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10

// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output: 15

Yo solucion茅 el problema de las cantidades as铆:

  1. Cambi茅 la funci贸n addToCart: reviso si el elemento existe en el carrito. Si no existe lo a帽ado y le agreg贸 un atributo 鈥渃ant鈥 con valor en 1. Si ya existe aumento el contador del atributo mencionado.
  2. Similar en la funcion removeFromCart, si el contador es 1 elimino el elemento del carrito, de lo contrario reduzco el contador.
  3. En el componente Checkout muestro la cantidad de items por cada producto y reemplac茅 el bot贸n de eliminar por dos botones para incrementar o decrementar la cantidad de productos.
  4. Actualic茅 el total de la compra para que tenga en cuenta las cantidades.
// src/hooks/useInitialState.js
import React from 'react';
import products from '../initialState';
function useInitialState() {
    const [state, setState] = React.useState(products());
    function addToCart(payload) {
        const element = state.cart.findIndex(item => item.id === payload.id);
        let updateCart = [...state.cart];
        if (element > -1) {
            updateCart[element].cant += 1;
        }
        else {
            updateCart = [...updateCart,{...payload, cant: 1}];
        }
        setState({...state, cart: updateCart});
    }
    function removeFromCart(payload){
        const element = state.cart.findIndex(item => item.id === payload.id);
        let updateCart = [...state.cart];
        if(state.cart[element].cant > 1){
            updateCart[element].cant -= 1;
        }
        else{
            updateCart= updateCart.filter(item => item.id !== payload.id);
        }
        setState({...state,cart:updateCart});
    }
    return {
        addToCart,
        removeFromCart,
        state
    }
}
export default useInitialState;
//src/containers/Checkout.jsx
import React from 'react';
import { Link } from 'react-router-dom';
import AppContext from '../context/AppContext';
import '../styles/components/Checkout.css';
function Checkout() {
    const {state:{cart}, addToCart ,removeFromCart} = React.useContext(AppContext);
    const handleAddToCart = product => () => {
        addToCart(product);
    }
    const handleRemove = product => () => {
        removeFromCart(product);
    }
    const handleSumTotal = () => {
        const reducer = (accum, current) => accum + (current.price * current.cant);
        return cart.reduce(reducer,0);
    }
    return (
        <div className="Checkout">
            <div className="Checkout-content">
                {cart.length > 0 ? <h3>Lista de productos:</h3> : <h3>Sin Productos</h3> }
                {cart.map((item) => (<div className="Checkout-item" key={item.id}>
                    <div className="Checkout-element">
                        <h4>{item.title}</h4>
                        <p>$<span>{item.price}</span></p>
                    </div>
                    <button className="Checkout-item-add" type="button" onClick={handleAddToCart(item)}>+</button>
                    <span className="Checkout-item-cant">{item.cant}</span>
                    <button className="Checkout-item-remove" type="button" onClick={handleRemove(item)}>-</button>
                </div>))}
            </div>
            {cart.length > 0 && (<div className="Checkout-sidebar">
                <h3>Precio Total: $<span>{handleSumTotal()}</span></h3>
                <Link to="/checkout/information">
                    <button type="button">Continuar pedido</button>
                </Link>
            </div>)}
        </div>
    );
}
export default Checkout;
/* src/styles/components/Checkout.css */
.Checkout-item button {
  background-color: transparent;
  border: solid 1px #a0a0a0;
  outline: none;
}
.Checkout-item-add {
  border-radius: 5px 0px 0px 5px;
}
.Checkout-item-remove {
  border-radius: 0px 5px 5px 0px;
}
.Checkout-item-cant {
  width: 20px;
  text-align:center;
}

Leyendo los comentarios y analizando la aplicaci贸n comprob茅 que hay un problema cuando agregas mas de 1 mismo producto ya que si ya no quieres 5 camisetas y eliminas 1 pues se borran las 5, adem谩s, un 2do problema con este mismo caso es que te aparece un error en la consola indicando que se est谩 repitiendo el key de un elemento. Entonces ayudandome con los aportes de los compa帽eros lo logr茅 solucionar:

Checkout.jsx

car.map()

useInitialState.js

Les dejo un aporte para que no te borre todos los productos con el mismo id agregando una key a cada prducto del cart:
En useInitialState:

import { useState } from 'react';
import initialState from '../initialState';

const useInitialState = () => {
  const [state, setState] = useState(initialState);

  const addToCart = (payload) => {
    const newProduct = {
      key: state.cart.length,
      ...payload,
    };
    setState({
      ...state,
      cart: [...state.cart, newProduct],
    });
  };
  const removeFromCart = (payload) => {
    setState({
      ...state,
      cart: state.cart.filter((item) => item.key !== payload.key),
    });
  };

  return {
    addToCart,
    removeFromCart,
    state,
  };
};

export default useInitialState;

Para arreglar el bug de que se eliminan todos los productos del mismo tipo cuanto intentas eliminar solo uno, cree un contador, y con ese contador elimino los productos, env茅s de hacerlo con el id del producto

import { useState } from 'react'
import initialState from '../initialState'

const useInitialState = () => {
  const [counter, setCounter] = useState(0)
  const addCounter = () => {
    setCounter(counter + 1)
    return counter
  }
  const [state, setState] = useState(initialState)

  const addToCart = payload => {
    setState({
      ...state,
      cart: [...state.cart,{...payload,index: addCounter()}]
    })
  }

  const removeFromCart = payload => {
    setState({
      ...state,
      cart: state.cart.filter(item => item.index !== payload.index)
    })
  }

  return{
    removeFromCart,
    addToCart,
    state
  }
}

export default useInitialState

Si est谩s en el minuto 5:07, y quieres entender m谩s de la funci贸n handleSumTotal**_ sin tener que hacer todo el curso de Redux_**, tal vez te sirva como a m铆 este art铆culo corto donde explican lo que es el .reducer en un array, y el useReducer, ya que la definici贸n que vimos en la clase pasada puede ser un poco dif铆cil de aplicar en este caso espec铆fico.

Saludos!

Estoy sorprendido, esto es bastante din谩mico y entendible

Solucion de Bug al agregar el mismo producto varias veces

Como muchos ya han notado en la app que estamos desarrollando hay un bug ya que si agregamos por ejemplo 3 Mug, pero nos arrepentimos de comprar 3 y eliminamos 1 veremos que se eliminaran los 3 ya que estamos filtrando por el ID del producto.

Usando uuid para generar Id鈥檚 unicos

Este problema podemos resolverlo agregandole un ID unico a cada producto de nuestro array cart, esta es la mandera en la que yo implemente la solucion y espero sea de ayuda

Instalar uuid

  • Primero instalaremos con npm npm i uuid
  • Posteriormente lo importaremos dentro de nuestro proyecto, para ser especificos en en componente de products, donde tenemos la logica de handleAddToCart import {v4 as uuidv4} from 'uuid';
  • Una ves ya agregamos el elemento a nuestro componente utilizaremos la siguiente logica para agregar un ID unico a cada nuevo producto en el carrito
    const handleAddToCart = (product)=>{ const idCart = uuidv4(); const newProduct = {...product, cartId: idCart}; addToCart(newProduct); }

Espero sea de ayuda esta implementacion para resolver el bug, recuerden que usar el index es una mala practica para identificar un elemento dentro de un array.

Creo que algunos han podido notar el bug de eliminar productos del carrito, eso se puede solucionar si sobreescribimos el id usando alguna librer铆a que genere ids 煤nicos por nosotros, como nanoid:

import { useState } from 'react'
import { nanoid } from 'nanoid'

import initialState from 'initialState'

const useInitialState = () => {
  const [state, setState] = useState({
    ...initialState,
    products: initialState.products.map(p => ({ ...p, id: nanoid() }))
  })

  const addToCart = (product): void => {
    setState({
      ...state,
      cart: state.cart.concat({
        ...product,
        id: nanoid()
      })
    })
  }

  const removeFromCart = (product): void => {
    setState({
      ...state,
      cart: state.cart.filter(p => p.id !== product.id)
    })
  }

  return { state, addToCart, removeFromCart }
}

export default useInitialState

Se que es mejor c贸digo entendible, pero si quieren saber la sumatoria de un key espec铆fico de un array con objetos, pueden usar esto sin necesidad de escribir todo el c贸digo escrito en clase c:, es lo mismo en menos c贸digo :

const totalPrice = cart.reduce((a, b) => a + (b.price || 0), 0);

Veo innecesario el template string ya que est谩 en una etiqueta y f谩cilmente se deja tal cual, solo usar llaves y listo.

<div className="Checkout-sidebar">
                <h3>Precio Total  $: {handleSumTotal()}</h3>
                <Link to="/checkout/information">
                    <button type="button">Continuar pedido</button>
                </Link>  
            </div>```

Increible, me case con Redux que ni siqueira le habia dado una posibilidad a useContext y este curso esta logrando que cambie de idea鈥

Que tal comunity?
En typescript tengo un error en el compilaror que es el siguiente: El error se situa en el archivo useInitialstate.tsx en la linea 28.

error TS2548: Type 'ObjectState[] | undefined' is not an array type or does not have a 'Symbol.iterator' method that returns an iterator.

Lo he intendado buscar por el numero de referencia pero nada.
Aqui les dejo mi ultimo commit que es donde se implementa useContext: commit

Tengo las siguientes preguntas:

  • Por que si le asigno el onClick al icono en vez del boton el evento no se ejecuta debidamente?
  • Se puede asignar el siguiente codigo : onClick={()=>handleRemoveFromCart} para una mejor estetica o afecta en diferente medida a : const handleRemoveFromCart = product =>()=>{}??
  • Respecto a cuando se repiten items, es una mala practica por parte del backen el que no se asigne una propiedad "cantidad"
    Gracias :ha

Hola a todos, me pueden ayudar con un error de Visual Studio Code que no s茅 realmente como solucionar.

Por cierto, el proyecto compila bien y no da ning煤n tipo de error en consola. Solo sale ese detalle en el editor.

Resulta que cuando quiero hacer el destructing de los elementos del context me sale este error:

Y si le doy a Quick fix lo 煤nico que hace es agregar un l铆nea de c贸digo para ignorar el error pero en teor铆a no lo soluciona sino que agrega un l铆nea de c贸digo que no quiero tener en mi proyecto.

Me pueden indicar c贸mo puedo corregir esto.

C谩lculo del costo total en una l铆nea:

const totalPrice = state.cart.reduce((sum, item) => sum + item.price, 0);

Esta fue mi solucion para el bug, no se si sea buena practica o este mal, me pueden decir

 const addToCart = (payload) => {
    if (State.cart.some((n) => n.id === payload.id)) {
      setState({
        ...State,
        cart: [...State.cart, { ...payload, id: Math.random() }],
      });
    } else {
      setState({
        ...State,
        cart: [...State.cart, payload],
      });
    }
  };

Versi贸n optimizada de **handleSumTotal **:

	const handleSumTotal = () => {
		return cart.reduce((prev, c) =>  prev + c.price, 0);
	}

Agregu茅 en modo oscuro y modo claro, que detecta s铆 el navegador esta en alguno de esos modos.

body {
    background-color: var(--bg-color);
    color: var(--text-color);
  }
  @media (prefers-color-scheme: dark){
    :root{
      --bg-color: #000;
      --text-color: #fff;
    }
  }
  
  @media (prefers-color-scheme: light){
    
  }

para que les funcione, deben importarlo al archivo App.jsx de routes

馃憖No olviden agregar una sentencia por si la lista de cart es esta vacia porque sino genera bugs. Asi lo hice yo:

{cart.length <= 0 ? (<h1>No hay nada</h1>) : (
          cart.map((product, i) => {
            return (
              <div key={product.id + i} className="Checkout-item">
                <div className="Checkout-element">
                  <h4>{product.title}</h4>
                  <span>${product.price}</span>
                </div>
                <button onClick={() => handleRemove(i)} type="button">
                  <TrashIcon />
                </button>
              </div>
            );
          })
        )}

Min 5:05 - 5:20. Es la nueva forma que tienen los profes para decir 鈥淓so lo debieron ver el a帽o pasado, alumnos鈥 馃槕
.
Yo pase por ese curso igualmente y aun as铆 no considero que pueda entender del todo aquella funcion.
.
Mas me parece a mi que el profe ya aplica esa funcion por experiencia mas que sepa como explicarla.
.
Aun as铆 cuando hayamos ya curso ese curso que nos recomienda, no esta dem谩s, en mi opini贸n explicar este tipo de l贸gica que viene mezclada ya con una sintaxis bastante reducida, que es propia del lenguaje.
.
Agradezco enormemente a los compa帽eros que tuvieron la misma inquietud y preguntaron, y tambi茅n a los/as compa帽eros que supieron explicar de manera mas clara lo que el profe paso por 鈥渙bvio鈥.

隆Hola! Estoy siguiendo este curso, pero usando NextJS + TypeScript + Tailwind + Zustand. A煤n no lo termino, pero voy agregando poco a poco las cosas que vamos viendo.

Si les interesa ver el proyecto hasta esta clase, les dejo el link del proyecto: https://github.com/d4vsanchez/platzi-conf-merch/tree/create-shopping-cart

Us茅 Zustand en lugar del contexto simplemente porque me encanta la librer铆a. Tiene una API sencilla, permite tener un estado global sin usar contexto (usa Pub/Sub), usa Hooks como forma principal de consumo, tiene menos boilerplate y es exageradamente f谩cil de extender a futuro.

Una forma de resolver el bug, cuando se a帽ade m谩s elementos del mismo ID, pero no la e implementado solo se me ocurrio, es cuando se leccionar un producto para enviarlo al carrito deveria agregar un objeto con el producto y la cantidad seleccionada para que en el estado global el cart quede asi

cart: [
      {
         item:{
            'id': '1',
            'image': 'https://arepa.s3.amazonaws.com/camiseta.png',
            'title': 'Camiseta',
            'price': 25,
            'description': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
         },
         ctn:0
      },
      {
         item:{
            'id': '1',
            'image': 'https://arepa.s3.amazonaws.com/camiseta.png',
            'title': 'Camiseta',
            'price': 25,
            'description': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
         },
         ctn:0
      },
]

ya despues le pueden dar la logica es un poco mas complicado pero creo que podria ser una opcion

d