Dejo como complemento este paquete que nos ayuda a construir formularios: https://react-hook-form.com/
¡Bienvenida! Este es un curso especial de React Hooks
¿Qué aprenderás en el Curso Profesional de React Hooks?
¿Qué son los React Hooks y cómo cambian el desarrollo con React?
Introducción a React Hooks
useState: estado en componentes creados como funciones
useEffect: olvida el ciclo de vida, ahora piensa en efectos
useContext: la fusión de React Hooks y React Context
useReducer: como useState, pero más escalable
¿Qué es memoization? Programación funcional en JavaScript
useMemo: evita cálculos innecesarios en componentes
useRef: manejo profesional de inputs y formularios
useCallback: evita cálculos innecesarios en funciones
Optimización de componentes en React con React.memo
Custom hooks: abstracción en la lógica de tus componentes
Third Party Custom Hooks de Redux y React Router
Configura un entorno de desarrollo profesional
Proyecto: análisis y retos de Platzi Conf Store
Git Hooks con Husky
Instalación de Webpack y Babel: presets, plugins y loaders
Configuración de Webpack 5 y webpack-dev-server
Configuración de Webpack 5 con loaders y estilos
Loaders de Webpack para Preprocesadores CSS
Flujo de desarrollo seguro y consistente con ESLint y Prettier
Estructura y creación de componentes para Platzi Conf Store
Arquitectura de vistas y componentes con React Router DOM
Maquetación y estilos del home
Maquetación y estilos de la lista de productos
Maquetación y estilos del formulario de checkout
Maquetación y estilos de la información del usuario
Maquetación y estilos del flujo de pago
Integración de íconos y conexión con React Router
Integración de React Hooks en Platzi Conf Merch
Creando nuestro primer custom hook
Implementando useContext en Platzi Conf Merch
useContext en la página de checkout
useRef en la página de checkout
Integrando third party custom hooks en Platzi Conf Merch
Configura mapas y pagos con PayPal y Google Maps
Paso a paso para conectar tu aplicación con la API de PayPal
Integración de pagos con la API de PayPal
Completando la integración de pagos con la API de PayPal
Paso a paso para conectar tu aplicación con la API de Google Maps
Integración de Google Maps en el mapa de checkout
Creando un Custom Hook para Google Maps
Estrategias de deployment profesional
Continuous integration y continuous delivery con GitHub Actions
Compra del dominio y despliega con Cloudflare
Optimización de aplicaciones web con React
Integración de React Helmet para mejorar el SEO con meta etiquetas
Análisis de performance con Google Lighthouse
Convierte tu aplicación de React en PWA
Bonus: trabaja con Strapi CMS para crear tu propia API
Crea una API con Strapi CMS y consúmela con React.js
¿Qué sigue en tu carrera profesional?
Próximos pasos para especializarte en frontend
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
Oscar Barajas Tavares
Aportes 15
Preguntas 9
Dejo como complemento este paquete que nos ayuda a construir formularios: https://react-hook-form.com/
Aunque Oscar para regresar de information a la página checkout ha usado el componente <Link> de React Router, lo ideal seria sacarle el máximo partido que podamos a las rutas.
Dentro de los componentes que renderizamos a través de Route existen en sus props un objeto llamado history, el cual podemos usar para regresar a la página anterior, ya que la app es una secuencia podemos usarla.
props.history.goBack()
Todas estas propiedades las explica Leonidas en el curso de React-Router
https://platzi.com/clases/1342-react-router/12868-manipulando-el-historial/
Una manera mas facil de crear el objeto en la constante buyer
es:
function handleSubmit() {
const formData = new FormData(form.current);
const buyer = Object.fromEntries(formData);
console.log(buyer);
}
Con esto nos ahorramos tener que escribir el objeto de forma literal, y además nos ahorramos mucho código.
Si estas usando el plugin de prettier en VS, y la parte del buyer le quita las comillas, puedes poner un comentario para que no formaté el codigo, asi,
const handleSubmit = () => {
const formData = new FormData(form.current);
// prettier-ignore
const buyer = {
'name': formData.get('name'),
'email': formData.get('email'),
'address': formData.get('address'),
'apto': formData.get('apto'),
'city': formData.get('city'),
'country': formData.get('country'),
'state': formData.get('state'),
'cp': formData.get('cp'),
'phone': formData.get('phone'),
};
addToBuyer(buyer);
};
Amigos yo lo hice creándome un custom hook useForm pero no se no se si la mejor forma de hacerlo
useForm.js
InformationScreen.jsx
constante buyer:
const buyer = {
'name': formData.get('name'),
'email': formData.get('email'),
'address': formData.get('address'),
'apto': formData.get('apto'),
'city': formData.get('city'),
'country': formData.get('country'),
'state': formData.get('state'),
'cp': formData.get('cp'),
'phone': formData.get('phone'),
}
pueden agregar esta linea en el webbpack.config.js para que el navegador se refresque y puedan hacer debug en el navegador
mode: 'development',
Aqui el codigo con la solicion del bug el momento de eliminar items de la lista.
useInitialState.js
import { useState } from 'react';
import initialState from '../initialState';
const useInitialState = () => {
const [state, setState] = useState(initialState);
const addToCart = (payload) => {
setState({
...state,
cart: [...state.cart, payload],
});
};
const removeFromCart = (payload, indexToRemove) => {
setState({
...state,
cart: state.cart.filter((_items, indexCurrent) => indexCurrent !== indexToRemove),
});
};
return {
addToCart,
removeFromCart,
state,
};
};
export default useInitialState;
Checkout.jsx
import React, { useContext } from 'react';
import { Link } from 'react-router-dom';
import AppContext from '../context/AppContext';
import '../styles/components/Checkout.css';
const Checkout = () => {
const { state, removeFromCart } = useContext(AppContext);
const { cart } = state;
const handleRemove = (product, index) => () => {
removeFromCart(product, index);
};
const handleSumTotal = () => {
const reducer = (accumulator, currentValue) => accumulator + currentValue.price;
const sum = cart.reduce(reducer, 0);
return sum;
};
return (
<div className='Checkout'>
<div className='Checkout-content'>
{cart.length > 0 ? <h3>Lista de pedidos:</h3> : <h3>Sin pedidos...</h3>}
{cart.map((item, index) => (
<div className='Checkout-item'>
<div className='Checkout-element'>
<h4>{item.title}</h4>
<span>
$
{item.price}
</span>
</div>
<button type='button' onClick={handleRemove(item, index)}>
<i className='fas fa-trash-alt' />
</button>
</div>
))}
</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>
);
};
export default Checkout;
Aqui el commit de la clase en Typescript:
Algo que hice en nuestro useInitial state fue agregar un useEffect que detecte cada vez que cambie nuestro carrito y lo re-ordene en orden alfabetico, de esta forma cuando obtenemos nuestros productos en el checkout o en la pagina de information siempre los productos iguales van a estar juntos aunque se hayan agregado en otro orden
import { useEffect, useState } from "react";
import initialState from "../initialState";
export default function useInitialState() {
const [state, setState] = useState(initialState);
// Using a useEffect to always re-sort our cart when it's updated
useEffect(() => {
const sortedCart = state.cart.sort((item, prevItem) => item.title > prevItem.title ? 1 : -1);
setState({...state, cart: sortedCart});
}, [state.cart]);
const addToCart = payload => {
// Assigning first a unique cart id to be able to delete only that element in the future
const randomNumber = Math.floor(Math.random() * Date.now());
const newItem = {...payload, cartId: `${payload.id}-${randomNumber}`};
setState({
...state,
cart: [...state.cart, newItem]
});
};
const removeFromCart = payload => {
setState({
...state,
cart: state.cart.filter(item => item.cartId !== payload.cartId)
});
};
const addToBuyer = payload => {
setState({
...state,
buyer: payload
});
};
return {
addToCart,
removeFromCart,
addToBuyer,
state
};
}
Aqui pueden ver como no importa en que orden agrege los articulos siempre salen acomodados en orden alfabetico
Con react 18 podemos hacer la navegación hacia atras con un hook de router
import {useNavigate} from 'react-router-dom';
Y podemos gacer una función paraceida a esta:
const handleClick = () => {
navigate('/checkout');
};
Y solo la llamamos en el onclick del elemento
Aporte codigo utilizado durante la clase:
.
src/containers/containers/Information
import React, { useRef, useContext} from "react";
import '../styles/components/Information.css';
import { Link } from "react-router-dom";
import AppContext from "../context/AppContext";
const Information = ()=>{
const { state, addToBuyer } = useContext(AppContext);
const form = useRef(null);
const { cart } = state;
const handlerSubmit = () =>{
const formData = new FormData(form.current);
const buyer = {
'name': formData.get('name'),
'email': formData.get('email'),
'address': formData.get('address'),
'apto': formData.get('apto'),
'city': formData.get('city'),
'country': formData.get('country'),
'state': formData.get('state'),
'cp': formData.get('cp'),
'phone': formData.get('phone'),
}
addToBuyer(buyer);
}
return (
<div className="Information">
<div className="Information-content">
<div className="Information-head">
<h2>Información de contacto:</h2>
</div>
<div className="Information-form">
<form ref={form}>
<input type="text" placeholder="Nombre completo" name="name" />
<input type="text" placeholder="Correo Electronico" name="email" />
<input type="text" placeholder="Direccion" name="addres" />
<input type="text" placeholder="apto" name="apto" />
<input type="text" placeholder="Ciudad" name="city" />
<input type="text" placeholder="Pais" name="country" />
<input type="text" placeholder="Estado" name="state" />
<input type="text" placeholder="Codigo postal" name="cp" />
<input type="text" placeholder="Telefono" name="phone" />
</form>
</div>
<div className="Information-buttons">
<div className="Information-back">
<Link to="/checkout">
Regresar
</Link>
</div>
<div className="Information-next">
<button onClick={handlerSubmit} type="button">pagar</button>
</div>
</div>
</div>
<div className="Information-sidebar">
<h3>Pedido:</h3>
{cart.map(item=>(
<div key={item.title} className="Information-item">
<div className="Information-element">
<h4>{item.title}</h4>
<span>${item.price}</span>
</div>
</div>
))}
</div>
</div>
)
}
export default Information;
src/hooks/useInitialState.js
import { useState } from "react";
import initialState from "../initialState";
const useInitialState = ()=>{
const [state, setState] = useState(initialState);
const addToCart = payload =>{
setState({
...state,
cart:[...state.cart,payload]
});
}
const removeFromCart = payload =>{
setState({
...state,
cart:state.cart.filter(item=>item.id!==payload.id)
})
}
const addToBuyer = payload=>{
setState({
...state,
buyer:[...state.buyer, payload]
})
}
return {
addToBuyer,
addToCart,
removeFromCart,
state,
}
};
export default useInitialState;
src/initialState.js
export default {
cart: [],
buyer:[],
products: [
{
'id': '1',
'image': 'https://arepa.s3.amazonaws.com/camiseta.png',
'title': 'Camiseta',
'price': 25,
'description': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
},
{
'id': '3',
'image': 'https://arepa.s3.amazonaws.com/mug.png',
'title': 'Mug',
'price': 10,
'description': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
},
{
'id': '4',
'image': 'https://arepa.s3.amazonaws.com/pin.png',
'title': 'Pin',
'price': 4,
'description': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
},
{
'id': '5',
'image': 'https://arepa.s3.amazonaws.com/stickers1.png',
'title': 'Stickers',
'price': 2,
'description': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
},
{
'id': '6',
'image': 'https://arepa.s3.amazonaws.com/stickers2.png',
'title': 'Stickers',
'price': 2,
'description': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
},
{
'id': '7',
'image': 'https://arepa.s3.amazonaws.com/hoodie.png',
'title': 'Hoodie',
'price': 35,
'description': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
},
],
};
Implemente el formulario con React Hook Form y es increíble lo fácil que se torna manipular el mismo
Al boton de pagar, si fuera un tag input le tenes que poner type=“button” pero si es una etiqueta button no hace falta
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?