Entendimiento en React [v2]

En este articulo entenderemos un poco de React

0. Que es y como funciona

Podemos entender que React es un creador de interfaces. Normalmente, y aquí, lo usaremos para desarrollo web; pero React no se limita a esto, ya que podemos desarrollar interfaces para apps, aplicaciones de escritorio.

Ahora bien, React no es un fremaework como tal, es una librería que facilita el desarrollo de interfaces.

Para desarrollar para el entorno web, debemos conocer JSX y un empaquetador, el cual sera vite.

0.1 Crear un app con React + JSX + SWC

En nuestro entorno de desarrollo, en la consola escribiremos:

Crear react proyect
1. Usamos primero el comando: npm create vite@latest
2. Seleccionamos las caracteristicas, como nombre, react y JS + swc.
3. Ahora en la carpeta le damos npm install
4. Y para iniciar npm run dev

Porque JS + SWC, sera posiblemente el nuevo estándar ya que es mas eficiente, eso si no cambia nada en JS. (También esta TS + SWC por si les gusta esta otra opción).

0.2 Funciones

Algo que se complica es usar una función con o sin paréntesis, es así:

  • Ejemplo: funcion - Así, pasa la función para que otro la use, pero no se activa.
  • Ejemplo: funcion() - Así, se ejecuta la función normalmente.


1. Componentes

Es la forma básica en React, todo son componentes, veremos un ejemplo:


En la imagen podemos decir que, cada cuadro es un componente: con imagen, un texto y una etiqueta. Ademas tenemos otro componente que es el titulo.

Los componentes son para facilitar el desarrollo y la reutilización, como el ejemplo anterior tenemos 3 elementos diferentes pero con la misma base.

Para crear un componente haremos:

import useLikeFood from "../hooks/useLikeFood";

export default function Food({id, nombre, precio, disponible, imagen, evento}) {

    const {gusta, MeGusta} = useLikeFood(id);

    function MostrarMas(e) {
        e.stopPropagation();
        evento(id);
    }

    function AgregarMeGusta() {
        MeGusta();
    }

    return(
        <div className="Food" >
            <figure onClick={AgregarMeGusta} className= {gusta ? "Food_like_yes" : "Food_like"} ></figure>
            <h3>{nombre}</h3>
            <p>${precio}</p>
            <img src={imagen} alt="" />
            <button onClick= {MostrarMas}>
                {/* <Link to={`/detalle/${id}`}>Mas info</Link> */}
                Mas info
            </button>
        </div>
    );
}

Tenemos nuestras importaciones, luego el componente con sus props, funcionalidad del componente, luego se retorna (crea) el elemento con las variables.

Algo importante en términos, los elementos son diferentes a componentes.

  • Elemento: es un objeto fijo, en este caso HTML.
  • Componente: es un creador de elementos.

2. Estados

Son un elemento importante en React, ya que mantienen la informacion a lo largo del tiempo. Ya que las variables normales no mantienen su informacion cuando, por ejemplo, cambiamos de pagina.

Para crear una variable con estado:


import { useState } from "react";
const [foods, setFoods] = useState("Papitas");
 

Importamos el hook...

En una constante; ponemos el nombre de la variable; luego la función SET de la misma variable; y lo igualamos al hook importado y debemos asignarle un valor de inicio. (es importante que al usar los sets, se use exclusivamente una variable y no una operación)

Así, logramos que la variable no cambie durante el uso de la pagina. Y algo importante es que los estados al cambiar cambiaran los componentes, esta reactividad se enfoca exclusivamente en los componentes que cambiaron, dando optimización en la pagina con React. Esto lo podemos ver, cuando en una pagina web, cuando le damos a un elemento, solo cambia este y no se recarga toda la pagina.

Algo que puede pasar, es que cuando usamos un estado, ejemplo, queremos mostrar el ultima lista de productos. El problema es que tal vez no muestre el ultimo cambio, para evitar esto usaremos 'prevState':


const Funcion = (producto) => {
        setLista(prevState => prevState.filter(item => item.id !== producto.id))
}
 


2.1 Hooks

Son herramientas pequeñas reutilizables. Nosotros mismos podemos crearlas respetando la escritura: useEjemploHook. Ejemplo: el mismo useState.

2.1.1 UseEffect

Es un hook, el cual ejecuta un código (callback) hecho por nosotros, según un parámetro dado, ejemplo:


import { useEffect } from "react"

  useEffect(() => {
    console.log("Ejecutando el useEffect");
   
  }, [var1, var2])

Entonces, el ejemplo anterior el hook se ejecuta cada vez que alguna de las variables 'var1' o 'var2' cambie.

El hook, en su segundo parámetro puede recibir:

  • Una o mas variables: cada vez que alguna de estas cambie, se ejecuta el callback.
  • Lista vacía: otra forma, la cual ejecuta el código, cada vez que hay un cambio.

Un ejemplo clásico para obtener datos de una API:

 
useEffect(() => {        

        fetch("URL API")
            .then(res => res.json())
            .then(data => setData(data))
            .catch(err => console.log(err))
    }, []);
 

Algo importante, es que el hook debe cumplir una única tarea, no varias.

2.1.2 useRef

Guardar algo, la cual se mantiene mientras viva el componente, y cuando cambia no renderiza, esto evita el reinicio de la referencia.

Por ejemplo, tenemos un sistema de búsqueda y queremos que no se pueda hacer 2 veces la misma búsqueda. 


const previousSearch = useRef(search)

if(search !== previousSearch.current) {
    // Consumir API ejemplo
}
 

Entonces, se guarda la búsqueda (y como 'useRef', no cambia aunque se renderize el componente, que es lo que hace en cada búsqueda, podremos saber si es lo mismo o no)


2.1.3 useMemo

Es parecido a useEffect, necesita una función callback y un parámetro de cambio. Usado para optimizar renderizados en la pagina. Entonces, si el parámetro cambia: ejecuta la función. Si no cambia: no se ejecuta. 

La diferencia, que memoriza lo obtenido de esta función y lo guarda hasta que el parámetro cambie. Ejemplo:


const filtro = useMemo(() => {
        return // Retornar algo a guardar
    }, [variable])
 

2.1.4 useId

En React al usar tantos  componentes, algunos de las etiquetas usan id, por ejemplo un formulario, crear múltiples elementos, entonces, entre mas grande la aplicación los id podrían repetirse y ejecutar acciones imprevistas dañando la web. Lo evitaremos con este hook.

const precioId = useId()
   
    return(
            <div>
                <label htmlFor={precioId}>Precio:</label>
                <input
                    type="range"
                    id={precioId}
                    min="0"
                    max="4000"
                />
            </div> )

En el ejemplo anterior, creamos un id único en nuestro pequeño formulario, evitamos en un futuro repetir el id y dañar el sistema y también hacer cosas raras, como usar un randomizador, o escribir un id largo y complicado, etc...

 

2.1.5 useContext

Es una forma de evitar "prop drillimg" (por ejemplo pasar un prop a travez de componentes padres uno y otra vez, aunque no lo usen) y facilitar el uso de recursos en lo lugares donde realmente se necesitan y no hacer recorridos largos pasando un prop.

'UseContext', crea un contexto, el cual es como una "variable" la cual guarda algo, y debemos definir quienes pueden usarla (como el encapsulamiento) y estos componentes la podrán usar. 

Primero creamos el contexto:


import { createContext } from "react";
/* EL contexto
    Primero se crea el contexto, luego
    Crear el provider para proveer el contexto deseado, en este caso el objeto
    Para despues, en este caso, hacer que toda la aplicacion acceda al contexto, ver main.jsx
   
*/

export const FiltroContexto = createContext()
 
export function FiltroProveedor({children}) {
    return (
        <FiltroContexto.Provider value = {{
            categoria: "Todos",
            minPrecio: 0
        }}
        >
            {children}
        </FiltroContexto.Provider>
    )
}
 

como vemos, se crea un componente el cual tiene un 'value' el cual es donde guardaremos lo consultable.

 

Ahora debemos definir quienes lo podrán usar, es el ejemplo le daremos permiso a toda la app (en main.jsx):

 
createRoot(document.getElementById('root')).render(
  <FiltroProveedor>
    <App />
  </FiltroProveedor>,
)


Para consumir el contexto, en un componente autorizado, haremos:


const filtro = useContext(FiltroContexto)
 

PERO, el contexto es estático, no es mutable es perfecto para cosas que no van a cambiar.

Pero si queremos que cambie, debemos hacer esto otro: 


export function FiltroProveedor({children}) { // El proveedor
    const [filtro, setFiltro] = useState({
        categoria: "Todos",
        minPrecio: 0
    })

    return (
        <FiltroContexto.Provider value = {{
            filtro,
            setFiltro
        }}
        >
            {children}
        </FiltroContexto.Provider>
    )
}
 

Cambiamos la función proveedora, agregando un estado y el 'value' con los valores del estado.

[Se mantiene los permisos de uso] 

Y para consumir ahora haremos:


const {filtro, setFiltro} = useContext(FiltroContexto)

Entonces ya podemos cambiar y usar el contexto fácilmente, y se convirtió el contexto en mutable.

 

2.1.6 UseReducer

Es un hook para reducir o separar funciones o procesos, mantenerlos organizados y aparte, ayuda al como se ve el código, y es de fácil acceso. Evitando que la logica este dentro del componente: como estilo de arquitectura MVC: modelo-vista-controlador.

Además, es para separar la lógica en una funcion aparte, la cual se vuelve mas fácil de depurar. Y, otros pueden obtener esta informacion y usarla.

Importante, esto solo se debería usar, cuando hay muchos estados que se afecten y tener que separar la lógica. 

Para crear el hook, importamos "useReducer", luego escribimos: 


function Reducer (state, action) {
    const {type: type, payload: payload} = action

    switch(type) {
        // Las operaciones
    }

    return state // <- El resultado
}
 

La funcion de reducción, recibe un estado inicial "state", y el objeto (o lo que se va a usar) "action".

Creamos un objeto con desestructuracion para obtener el objeto o la informacion enviada:

  • El tipo, determina la accion a realizar. 
  • El "payload" (carga útil) o la informacion objeto, etc; a usar. 

Luego, con una condicion, donde haremos la lógica, ejemplo:

 
case "REMOVER_OBJ": {
            const {id} = payload
            return state.filter(item => item.id !== id)
        }
 

Esto va adentro del condicional... 

Y con un string definimos el tipo de operacion. 

Luego la logica de esta parte (como se ve, el "payload" es de donde obtenemos lo necesario). 

Por ultimo retornamos el resultado.

Para usarlo

En el componente obtenemos el hook:


const estadoInicial = [] // <- Afuera del componente

const [state, dispatch] = useReducer(reducer, estadoInicial)
 

Y usamos asi:


const LimpiarCarroProducto = obj => dispatch({
        type: "REMOVER_OBJ",
        payload: obj
    })
 

El "dispatch" es donde llamaremos y enviaremos lo necesario para ejecutar.

El estado es donde se guarda todo.