No tienes acceso a esta clase

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

Curso Profesional de Next.js

Curso Profesional de Next.js

Oscar Barajas Tavares

Oscar Barajas Tavares

Creaci贸n del componente Alert

22/31
Recursos

Aportes 10

Preguntas 3

Ordenar por:

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

o inicia sesi贸n.

Les dejo el Alert.js

import { XCircleIcon } from '@heroicons/react/solid';
<div x-data className="bg-indigo-100 p-5 w-full rounded mb-8">
					<div className="flex space-x-3">
						<div className="flex-1 leading-tight text-sm text-black font-medium">{alert.message}</div>
						<button type="button">
							<XCircleIcon className="w-6 h-6 text-gray-600" onClick={handleClose} />
						</button>
					</div>
				</div>```

si a alguno le sirve y entiende mi resumen de la clase鈥 aca esta xD

Clase #22: Creaci贸n del componente Alert 22/31 馃攭馃挰


Conceptos necesarios para entender la clase: 馃毃

驴Qu茅 es scope?

El scope en JavaScript representa el alcance que tendr谩n las variables del c贸digo, es decir, indicar谩 si las variables son locales (existen dentro de una funci贸n en espec铆fico) o globales (existen para todo el c贸digo). Fuente: aqu铆

驴Qu茅 es Alpine.js?

Alpine.js ofrece las propiedades reactivas y declarativas de grandes frameworks como Vue o React con un coste mucho menor. Mantiene el DOM, pudiendo mejorar y perfeccionar el comportamiento como m谩s convenga. Podr铆amos considerarlo como un Tailwind para JavaScript. Fuente: aqu铆

驴Qu茅 es el componente x-data?

En la documentaci贸n de Alpine.js (enlace: aqu铆), define a x-data como un fragmento de HTML definido por un componente de Alpine cuya funci贸n es proporcionar datos de react para que ese componente haga referencia, es decir x-data declara un nuevo scope del componente usado.


Por ejemplo si tenemos:

<div x-data="temp()" x-init="init()">
 	<!-- todo -->
</div>


Ac谩 lo que indica es que el scope de este componente ser谩 la funcion temp() y que solo estar谩 disponible para todo aquello que est茅 dentro de esa funci贸n donde fue declarado.

En cambio si tenemos lo siguiente:

<div x-data="{ open: true }">
	<button @click="open = false" x-show="open">Hide Me</button>
</div>


El 鈥淐ontenido鈥 depende del open captador al momento del evento del bot贸n, as铆 que las variables locales vivir谩n dependiendo del evento de open.

Se puede tener componentes sin datos, es decir el x-data tiene un objeto vac铆o, sin embargo al colocar x-data indica que es un componente Alpine ya que todos los componentes de 茅ste tipo empiezan con x-data:

<div x-data="{}">
//o tambi茅n puede ser as铆:
<div x-data>

Operador l贸gico AND (&&)

Este operador l贸gico compara dos expresiones. Si la primera se eval煤a como 鈥渧erdadera鈥 (truthy), la sentencia devolver谩 el valor de la segunda expresi贸n. Si la primera expresi贸n se eval煤a como 鈥渇alsa鈥(falsy), la sentencia devolver谩 el valor de la primera expresi贸n. Fuente: aqu铆


Ejemplo:

true && true //devuelve el segundo valor, verdadero
true && false //devuelve el segundo valor, falso
false && false //devuelve el primer valor, falso
123 && 'abc' // devuelve el segundo valor, 'abc'
'abc' && null //devuelve el segundo valor, nulo
undefined && 'abc' //devuelve el primer valor, indefinido
0 && false //devuelve el primer valor, 0


Continuando con el Proyecto: 馃敤

Vamos a crear un alert cuando un usuario ha agregado de forma correcta un producto. Para ello vamos paso a paso, primero debemos mostrar todos los productos cuando entramos a local: http://localhost:3000/dashboard/products

En el archivo products.js de la ruta src/pages/dashboard se agrega el import de endPoints:

import endPoints from '@services/api';


Luego por cuesti贸n de buenas pr谩cticas se ten铆a que la funci贸n products() estaba en min煤scula la letra inicial, mejor colocar en may煤scula la inicial: export default function Produts()

Se agrega el useEffect para traer todos los productos:

//useEffect cuando los componentes hagan render se pueda traer los productos
  useEffect(() => {
    async function getProducts(){
      const response = await axios.get(endPoints.products.allProducts);
      setProducts(response.data);
    }
    try{
      getProducts(); //Se hace render de todos los productos que existen en la API
    } catch (error){
      console.log(error);
    }
  }, [])


En el archivo index.js de la ruta src/services/api se agrega en la parte de products allProducts:

//allProducts trae todos los productos sin el limit y offset
        allProducts: `${API}/api/${VERSION}/products/`,


Guardamos y ejecutamos en consola npm run dev y debe salir todos los productos cuando entramos a local/dashboard/products

Implementamos la estructura del alert, se crea el archivo Alert.js en la ruta src/commons, la l贸gica queda:

import { XCircleIcon } from '@heroicons/react/solid'; //import del 铆cono XCircleIcon de HeroIcons

//Componente Alert
const Alert = ({ alert, handleClose }) => {
  if (alert && alert?.autoClose) {
    setTimeout(() => {
      handleClose();
    }, 9000); //El alert se cierra autom谩tiamente despu茅s de los 9 segundos con la funci贸n autoClose
  }

  /*En return se encapsula con un fragment <> </> lo que se quiere mostrar con el alert
    Se hace la validaci贸n del alert con el operador && que cuando solo est谩 activo
    se muestra el recuadro con el mensaje, es decir la segunda expresi贸n*/
  return (
    <>
      {alert?.active && (
        <div x-data className="bg-indigo-100 p-5 w-full rounded mb-8">
          <div className="flex space-x-3">
            <div className="flex-1 leading-tight text-sm text-black font-medium">{alert.message}</div>
            <button type="button">
              <XCircleIcon className="w-6 h-6 text-gray-600" onClick={handleClose} />
            </button>
          </div>
        </div>
      )}
    </>
  );
};

export default Alert;


Para crear la estructura necesaria para compartir diferentes estados de los componentes en la ruta src/hooks se crea el archivo useAlert.js la l贸gica queda:

import { useState } from 'react';

//useAlert permite tener opciones
const useAlert = (options) => {
  const defaultOptions = {
    active: false, //Queda inicalizado en false
    message: '',
    type: '', //para la data inicial se necesita que est茅 primero vac铆o
    autoClose: true,
  };
  const [alert, setAlert] = useState({
    ...defaultOptions, //con .... se destructura los valores
    ...options,
  });

  const toggleAlert = () => {
    setAlert(!alert.active); //cambia el estado seg煤n sea el caso
  };

  return {
    alert,
    setAlert,
    toggleAlert,
  };
};

export default useAlert;


Se debe integrar en products.js (src/pages/dashboard) con los import en la cabecera de Alert y useAlert:

import Alert from '@common/Alert';
import useAlert from '@hooks/useAlert';


En la funci贸n Products se crean las constantes necesaria para la etiqueta de <Alert />

const { alert, setAlert, toggleAlert} = useAlert;


Dentro del return al principio se agrega la etiqueta de <Alert />

<Alert alert={alert} handleClose={toggleAlert}/>


Tambi茅n se agregan las propiedades a FormProduct para que se genere el alert cuando se agregue un nuevo producto:

<Modal open={open} setOpen={setOpen}>
<FormProduct setOpen={setOpen} setAlert={setAlert}/>
</Modal>


Por 煤ltimo, se agrega el alert en useEffect en los corchetes [] al finalizar, para que al hacer el fecth, se garantice que vuelva a traer los productos, es decir, se agrega el producto, se genera el alert, el alert se cierra y volvemos a tener la lista de todos los productos:

}, [alert])

Esta librer铆a te deja hacer algo similar m谩s f谩cilmente

Siempre me he preguntado cu谩l ser谩 la forma en la que trabaja una empresa como Facebook鈥 c贸mo utilizar谩 react, respecto a la estructuraci贸n de sus carpetas, ser谩 que tienen muuuuuuuuuchos hooks? muuuuuuuuuuuuuuuuuuuuuuchos common components? siendo una aplicaci贸n tan robusta, siempre me ha intrigado como manejan ellos este sin fin de archivos.

Hola en el componente Alert me estaba presentando un warning, esto es por que el atributo x-data requiere un valor boleano. Lo solucion茅 as铆:

 <div x-data={alert.active} className="bg-indigo-100 p-5 w-full rounded mb-8">

Y nuestro querido custom hook [useFetch] al olvido鈥 UnU

a帽adimos un nuevo endPoint
src/services/api/index.js:

const API = process.env.NEXT_PUBLIC_API_URL;
const VERSION = process.env.NEXT_PUBLIC_API_VERSION;

const endPoints = { 
    auth: {
        ....
    },
    products: {
        ....
        allProducts: `${API}/api/${VERSION}/products`,
        ....
    },
    categories: {
        ....
    },
    files: {
        ....
    },
}; 

export default endPoints

creamos un nuevo hook
src/hooks/useAlert.js:

import { useState } from "react";

const useAlert = (options)=> {
    const defaultOptions = {
        active: false, 
        message: '',
        type: '',
        autoClose: true,
    }

    const [alert, setAlert] = useState({
        ...defaultOptions,
        ...options
    })

    const toggleAlert = ()=> {
        setAlert(!alert.active);
    }

    return {
        alert, 
        setAlert,
        toggleAlert
    }
}

export default useAlert;

creamos un nuevo componente
src/common/Alert.js:

import { XCircleIcon } from '@heroicons/react/solid';

const Alert = ({ alert, handleClose }) => {
  if (alert && alert?.autoClose) {
    setTimeout(() => {
      handleClose();
    }, 9000);
  }

  return (
    <>
      {alert?.active && (
        <div x-data className="bg-indigo-100 p-5 w-full rounded mb-8">
          <div className="flex space-x-3">
            <div className="flex-1 leading-tight text-sm text-black font-medium">{alert.message}</div>
            <button type="button">
              <XCircleIcon className="w-6 h-6 text-gray-600" onClick={handleClose} />
            </button>
          </div>
        </div>
      )}
    </>
  );
};

export default Alert;

integramos todo a la secci贸n de productos, creando toda la l贸gica para hacer la petici贸n a ese endPoint anteriormente creado, render de los productos y parcialmente la l贸gica para mandar una alerta de confirmaci贸n
src/pages/dashboard/products.js:

import axios from "axios"; 
import endPoints from "@services/api"; 
import useAlert from "@hooks/useAlert"; 
import Alert from "@common/Alert"; 

export default function Products(){
    const [open, setOpen] = useState(false);
    const [products, setProducts] = useState([]);
    const { alert, setAlert, toggleAlert } = useAlert();  

    useEffect(()=> { 
        async function getProducts (){
            const response = await axios.get(endPoints.products.allProducts);
            setProducts(response.data);
        }

        try {
            getProducts();
        } catch (error){
            console.error(error);
        }
    }, [alert]) 
    
    return (
        <>
            <Alert alert={alert} handleClose={toggleAlert} /> 
            ....
            <Modal 
                open={open}
                setOpen={setOpen}
            >
                <FormProduct setOpen={setOpen} setAlert={setAlert} /> 
            </Modal>
        </>
    )
}

Parte del codigo

import { XCircleIcon } from '@heroicons/react/solid';

const Alert = ({ alert, handleClose }) => {
  if (alert && alert?.autoClose) {
    setTimeout(() => {
      handleClose();
    }, 9000);
  }

  return (
    <>
      {alert?.active && (
        <div x-data className="bg-indigo-100 p-5 w-full rounded mb-8">
          <div className="flex space-x-3">
            <div className="flex-1 leading-tight text-sm text-black font-medium">{alert.message}</div>
            <button type="button">
              <XCircleIcon className="w-6 h-6 text-gray-600" onClick={handleClose} />
            </button>
          </div>
        </div>
      )}
    </>
  );
};

export default Alert;