No tienes acceso a esta clase

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

Adquiere por un a帽o todos los cursos, escuelas y certificados por un precio especial.

Antes: $249

Currency
$219/a帽o

Paga en 4 cuotas sin intereses

Paga en 4 cuotas sin intereses
Comprar ahora

Termina en:

0D
2H
35M
45S
Curso Profesional de Next.js

Curso Profesional de Next.js

Oscar Barajas Tavares

Oscar Barajas Tavares

Creaci贸n y cargado de datos para actualizar un producto

25/31
Recursos

Aportes 14

Preguntas 3

Ordenar por:

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

o inicia sesi贸n.

El problema en este caso ser铆a que podr铆amos colocar una ID que no exista y eso romper铆a la p谩gina. Para evitarlo podemos crear una constante notFound

const [notFound, setNotFound] = useState(false);

Luego crear un catch que, en el caso de que no encuentre la ID, cambie notFound a true

async function getProduct() {
			try {
				const response = await axios.get(endPoints.products.getProduct(id));

				if (response) {
					setProduct(response.data);
				}
			} catch (error) {
				console.log(error);
				setNotFound(true);
			}
		}

Y por 煤ltimo condicionar nuestro return al caso de que encuentre o no el ID

return notFound ? <div> Product Not Found </div> : <FormProduct product={product} />;

Para esas personas que se estan haciendo un lio para cambiar el value del select de la categor铆a es tan simple como poner esto:

  // Se usa el hook del cambio de estado sobre los productos
  useEffect(() => {
    // Se coge la referencia al nodo de HTML del select
    const categoryTag = document.querySelector('#category');
    // Se cambiar el valor del nodo por el valor del id; eso hace que el valor del <option> cambie tambien
    categoryTag.value = product?.category?.id;
  }, [product]);

Recordemos que React y Next siguen siendo JavaScript, no hay que inventar cosas raras para manipular el DOM

Clase # 25: Creaci贸n y cargado de datos para actualizar un producto 25/31 馃帹


Continuando con el Proyecto: 馃敡


Vamos a crear la estructura para actualizar uno de los productos por medio del navegador. Lo que se quiere es que el usuario al dar click en la opci贸n 鈥淓dit鈥 de cada producto, haga el enlace con la referencia de editar el producto y aparezca el cuadro con los campos para editar los atributos del producto.

Vamos a VSC y en la carpeta de la ruta principal de las p谩ginas: src/pages creamos una nueva carpeta llamada edit que contendr谩 la ruta din谩mica que por medio de un id identificar谩 a cu谩l producto se har谩 el cambio. Dentro de esa carpeta edit, se crea el archivo con corchetes [id].js 脡ste id din谩mico lo vamos a obtener por medio de la url, 茅ste id ser谩 capturado por useRouter() de next, que se har谩 una solicitud a la API para que pueda traer los datos del producto y de 茅sta forma hacer render dentro del formulario para poder cambiar cualquier elemento y luego guardarlo.

La l贸gica de [id].js queda:

import FormProduct from '@components/FormProduct';
import React, { useEffect, useState } from 'react';
import { useRouter } from 'next/router';
import axios from 'axios';
import endPoints from '@services/api';

//La funci贸n edit retorna el formulario para pasarle un nuevo valor del producto
export default function Edit() {
	const [product, setProduct] = useState({}); //Donde se almacena la informaci贸n
	const router = useRouter();

	useEffect(() => {
		const { id } = router.query;
		if (!router.isReady) return; //Si no tenemos el id, retorna y no hace el llamado
		//Se hace el llamado con getProduct
		async function getProduct() {
			const response = await axios.get(endPoints.products.getProduct(id));
			setProduct(response.data);
		}
		getProduct();
	}, [router?.isReady]); //Para saber que ya est谩 disponible

	return <FormProduct product={product} />;
}


Luego en el archivo FormProduct.js de la ruta src/components se agrega a la funci贸n Products el par谩metro product:

export default function FormProduct({setOpen, setAlert, product})


Dentro del return, en cada elemento del producto se le asigna el valor por defecto:

  • Para el nombre:
<input defaultValue={product?.title}

  • Para el precio:
<input defaultValue={product?.price}

  • Para la categor铆a:
<select
id="category"
name="category"
defaultValue={product?.categoryId}

  • Para la descripci贸n:
<textarea
defaultValue={product?.description}

  • Para la imagen:
<input defaultValue={product?.images}


Guardamos, corremos npm run dev vamos al navegador y colocamos la url: http://localhost:3000/dashboard/edit/7

Al final podemos cambiar el n煤mero que corresponde al id y sale el cuadro del Form para ingresar los datos a actualizar.

Los que quieran solucionar el problema de la categor铆a, cambien la sigueinte l铆nea:

defaultValue={product?.category?.id}

Por esto:

value={product?.category?.id.toString()}

.
Convierto el n煤mero de categor铆a a String porque el select espera un String pero lo recibido es un Number

Mmm yo creo que falta agregar algo, el tema de category no funciona bien ya que en el llamado obtenemos un objeto m谩s no un id, aparte de que el defaultValue en el select no funciona, se me ocurri贸 una manera de solucionarlo, agregamos un ref a ese select, luego cuando se cargue el componente agregamos un useEffect que escuche el cambio del producto con un switch:

const categorySelect = useRef(null)

  useEffect(() => {
    switch (product?.category?.id) {
      case 1:
        categorySelect.current.children[0].selected = true
        break
      case 2:
        categorySelect.current.children[1].selected = true
        break
      case 3:
        categorySelect.current.children[2].selected = true
        break
      case 4:
        categorySelect.current.children[3].selected = true
        break
      case 5:
        categorySelect.current.children[4].selected = true
        break
      default:
        break
    }
  }, [product])

As铆, cuando cargue el component, lograremos que aparezca con la categor铆a correcta.

yo le a帽ad铆 los casos de loading y error cuando hacemos la petici贸n, de esta forma podemos controlar los casos en los que se presente un problema al realizar la petici贸n o cuando esperamos por la data
src/pages/dashboard/edit/[id].js:

import React, { useEffect, useState } from 'react';
import { useRouter } from "next/router";
import axios from "axios";
import endPoints from "@services/api";
import FormProduct from "@components/FormProduct";

const Edit = () => {
    const router = useRouter();
    const [product, setProducts] = useState({});
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState(false);
    
    useEffect(()=> {
        setLoading(true);
        const { id } = router.query
        
        const getProduct = async ()=> {
            const response = await axios.get(endPoints.products.getProduct(id));
            return response.data
        }

        getProduct()
            .then(res => {
                setLoading(false);
                setProducts(res)
            })
            .catch(err => {
                setLoading(false);
                setError(true);
            })
    }, [router?.isReady])

    if (loading) {
        return <h1>Loading ...</h1>
    }   

    if (error){
        return <h1>Not Found: request failed</h1>
    }

    return (
        <FormProduct product={product} />
    );
}

export default Edit;

para el formulario, cambie el valor por defecto de las categor铆as, accediendo a la propiedad id de category

defaultValue={product?.category?.id}

Si hacen peticiones y el servidor les avienta un error 404 es porque probablemente el ID el cual est谩n solicitando ya no exista
,para ello podr铆an hacer una validaci贸n y ver si el ID primero existe y luego enviar la petici贸n. He visto que algunos compa帽eros ya han pasado el como hacerlo

Para manejar el caso en el que el id que se manda por la URL no exista, modifiqu茅 el useEffect de [id].js de la siguiente manera:
En pages cre茅 un archivo llamado notFound.js para crear esa ruta.

React.useEffect(() => {
    //El id se obtendr谩 desde la URL
    const {id} = router.query;
    //Si la ruta a煤n no est谩 lista, entonces el id tampoco
    if (!router.isReady) return;
    //Funci贸n para obtener la info del producto a partir del id
    const getProduct = async () => {
      const response = await axios.get(endPoints.products.getProduct(id));
      return response;
    };
    //Si la promesa se resuelve, asignamos la data a product
    getProduct()
      .then((response) => setProduct(response.data))
      //Si el id no existe, mandamos a notFound
      .catch((err) => router.push('/notFound'));
  }, [router?.isReady]);

Y para arreglar el problema con el select de las categor铆as agregu茅 lo siguiente:

FormProduct.jsx

const categorySelect = React.useRef(null);
React.useEffect(() => {
    if (product) {
      categorySelect.current.children[product?.category.id - 1].selected = true;
    }
  }, [product]);

Al mostrar los datos del producto a editar, no se selecciona la categor铆a correspondiente, lo que hice fue usar los hooks 鈥useEffect鈥 y 鈥useState鈥 para obtener la categor铆a del producto:

const [categoryDefaultValue, setCategoryDefaultValue] = useState(1);

useEffect(() => {
	if (product) {
		setCategoryDefaultValue(product?.category?.id);
	}
}, [product]);

Y en el select de la categor铆a agregu茅 en el 鈥value鈥 (en el 鈥defaultValue鈥 no funciona):

<select
	value={categoryDefaultValue}
	onChange={(e) => setCategoryDefaultValue(parseInt(e.target.value))}

El 鈥onChange鈥 es necesario, si no se agrega marca un error en consola de que debes agregarlo, ya que has definido directamente el 鈥value鈥 del select.

Con respecto al category yo lo resolvi a帽adiendo lo siguiente antes del return (鈥):

if (formRef?.current && product) {
//formData captura cada elemento del input
const formData = new FormData(formRef.current);
formData.set(鈥榗ategory鈥, product?.category?.id)
}

As铆 lo logr茅 con Next 13:

'use client'
import { useEffect, useState } from 'react'
import axios from 'axios'
import { endpoints } from '@/libs/endpoints.api' 
import { CreateProduct } from '@/types/Product'
import Form from '@/components/Form'

export default function Edit({ params }: { params: { id: string } }) {
  const [product, setProduct] = useState<CreateProduct>()
  const id =  params.id

  useEffect(() => {
    if (!id) return

    const getProduct = async () => {
      const response = await axios.get(endpoints.products.getProduct(id));
      console.log(id, response.data);
      setProduct(response.data)
    }
    getProduct();
  }, [id])

  return (
    <Form product={product} />
  )
}

Hasta ahora no hemos implementado nada de SSR o SSG. No es una de las razones por las cuales usar Next???

compas. Les paso como solucione el tema de los VALUE del SELECT por si les sirve.
coloquen el siguiente codigo y les va a andar.

  const categorySelectRef = useRef(null);
  useEffect(() => {
    categorySelectRef.current.value = product.categoryId;
  }, [product]);

No se olviden de poner el ref al
SELECT.

<select
                ref={categorySelectRef}
                id="category"
                name="category"
                autoComplete="category-name"
                className="..."
              >
	...
</select>

RECUERDEN QUE ES UNA MALA PRACTICA MANIPULAR DIRECTAMENTE EL DOM CUANDO SE USE REACT.

A meterle que si se puede!!馃憤馃榿

Solo quiero mencionar que en el form deben cambiar Toys por Shoes, ya que en la API es Shoes no Toys

              <select
                id="category"
                value={product?.category?.id.toString()}
                name="category"
                autoComplete="category-name"
                className="mt-1 block w-full py-2 px-3 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
              >
                <option value="1">Clothes</option>
                <option value="2">Electronics</option>
                <option value="3">Furniture</option>
                <option value="4">Shoes</option>
                <option value="5">Others</option>
              </select>
            </div>