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 “Contenido” 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 “verdadera” (truthy), la sentencia devolverá el valor de la segunda expresión. Si la primera expresión se evalúa como “falsa”(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;