0

I´m getting an error when trying to render an image in component.
I paste the code here.
Is it possible that I need a babel or webpack plugin?

In this component, the image rendering works fine:

import React from "react";

function ProductItem({ product }) {


  return product ? (
    <div>
      <div>
        <img src={product.images[0]} alt={product.title} />
      </div>
      <div>
        {product.title}
        <br />
        ${product.price}
      </div>
      <p>{product.description}</p>
    </div> 
  ) : <p>Loading Product... </p>;
}; 

export default ProductItem;

In this other component is where I have the problem.
ProductDetail.js

import React from "react";
import useGetProducts from "../hooks/useGetProducts";

const API = 'https://api.escuelajs.co/api/v1/products';

function ProductDetail() {

  const data = useGetProducts(`${API}/6`);

  return (
    <>
      {data.products
        ? 
        <>
            <h3>{data.products.title}</h3>
            <p>{data.products.description}</p>
            <div>
              <img src={data.products.images[0]} alt="title" />
            </div>
        </>
        : <h4>Loading...</h4>
      }
    </>
  );
}

export default ProductDetail;

enter image description here

Custom Hook with useEffect, the useGetProducts function is responsible for bringing the data from the API with the Axios library

import { useEffect, useState } from "react";
import axios from "axios";

const useGetProducts = (API) => {
  const [products, setProducts] = useState([])
  const [error, setError] = useState("");
  const [loaded, setLoaded] = useState(false);
  useEffect(() => {
    (async () => {
      try {
        const response = await axios(API);
        setProducts(response.data);
      } catch (error) {
        setError(error.message);
      } finally {
        setLoaded(true);
      }
    })();
  }, []);

  return { products, error, loaded };
};

export default useGetProducts
0

2 Answers 2

3

Your default state for products is [], so the conditional render data.products in ProductDetail.js always return true so you can change default state for products is null

const [products, setProducts] = useState(null);
Sign up to request clarification or add additional context in comments.

2 Comments

It works fine in ProductDetails.js. but in home (ProductList.js) {data.products.map((product) => ( <div key={product.id}> <ProductItem product={product} /> <div> <Link to={/product/${product.id}}>{product.title}</Link> </div> </div> ))} now I get this error Uncaught TypeError: Cannot read properties of null (reading 'map')
@alejandro try this {data.products && data.products.map(....} or {data.products?.map(....}
2

The first answer is correct, so I will not duplicate it, but I see room for improvement in your code/example.

Your useGetProducts hook is very easy to break and hard to reuse. If you will pass the wrong URL or the structure of the API will change it will break your code. Also, the hook is not very generic, cause you will need to create similar fn for each entity. My suggestion. Use react-query and separate functions for calling API. So it will look like this.

import { useQuery } from 'react-query'
import axios from 'axios'

export default function ProductPage() {
  const productResponse = useQuery('exchanges', () => getProduct('6'))
  const { isLoading, isError, data: product } = productResponse

  return (
    <div>
      {isLoading && <div>Loading...</div>}
      {isError && <div>Something went wrong :(</div>}

      {product && (
        <div>
          <h1>Product title: {product.title}</h1>

          <p>
            {product.images.map(imageSrc => (
              <img key={imageSrc} src={imageSrc} alt="" />
            ))}
          </p>
        </div>
      )}
    </div>
  )
}

interface Product {
  id: string
  title: string
  images: string[]
}

function getProduct(id: string): Promise<Product> {
  return axios
    .get(`https://api.escuelajs.co/api/v1/products/${id}`)
    .then(r => r.data)
}

PS. react-query requires additional configuration ( context provider, config, etc ). Please look into docs on how to use it.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.