Home > Mobile >  useFetch custom hook not working properly
useFetch custom hook not working properly

Time:11-18

I am working on react website. I have created one custom data fetching hook 'usePostFetch' as follows:

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

//axios
import axios from "axios";

const usePostFetch = () => {
  const [postData, setPostData] = useState([]);
  const [error, setError] = useState(null);
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    const getData = async () => {
      setIsLoading(true);
      try {
        const res = await axios.get("http://localhost:8000/Sell");
        const data = await res.data;
        setPostData(data);
        setIsLoading(false);
      } catch (error) {
        console.log("Error from fetch: "   error);
        setError(error.message);
        setIsLoading(false);
      }
    };
    getData();
  }, []);
  const values = [
    ...new Set(
      postData.map((post) => {
        return post.productType;
      })
    ),
  ];
  return { postData, values, error, isLoading };
};
export default usePostFetch;

I have a product page that renders when I click any of the links on the home page with a link "/product/:productId".productId is the id of clicked link product.

Product Page:

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

//react router dom
import { useParams } from "react-router";
//Hooks
import usePostFetch from "../../Hooks/usePostFetch";
//styles
import { Wrapper, Info, Discription } from "./Product.styles";
//Server
const Server = "http://localhost:8000";

const Product = () => {
  const { productId } = useParams();
  const { postData, isLoading, error } = usePostFetch();
  const [data, setData] = useState({});

  console.log(postData, isLoading, error);

  useEffect( () => {
    const fetchData = async () => {
      var value = await postData.filter((post) => {
        return post._id === productId;
      });
      console.log(value);
      setData(value);
    };
    fetchData();
  }, [postData]);

  return (
    <Wrapper>
      <Info>
        {isLoading && <h1> Loading.... </h1>}
        {error && <p>ERROR </p>}
        {console.log(data)}
        <img
          src={`${Server}/productImages/${data[0].productImage}`}
          alt={`${data[0].productName}`}
        />
        <div className="data">
          <h1>{data[0].productName}</h1>
          <h3>{data[0].productPrice}</h3>
        </div>
      </Info>
    </Wrapper>
  );
};

export default Product;

But when I go to that link I got data in console like this:
enter image description here

Because of these empty arrays, I got errors like this:
enter image description here

What can I do or what is wrong with my code?

CodePudding user response:

It appears you are reading state that doesn't exist yet. The initial data state is an empty object:

const [data, setData] = useState({});

And on the initial render you are attempting to read from a 0 property, which OFC is undefined still.

data[0] --> OK, undefined
data[0].productName --> NOT OK, throws error trying to access from undefined

You can conditionally render the data content when you know it's populated:

<Wrapper>
  <Info>
    {isLoading && <h1> Loading.... </h1>}
    {error && <p>ERROR </p>}
    {console.log(data)}
    {data[0] && (
      <img
        src={`${Server}/productImages/${data[0].productImage}`}
        alt={`${data[0].productName}`}
      />
      <div className="data">
        <h1>{data[0].productName}</h1>
        <h3>{data[0].productPrice}</h3>
      </div>
    )
  </Info>
</Wrapper>

Or you can just use the Optional Chaining operator to defend against null/undefined property accesses:

<Wrapper>
  <Info>
    {isLoading && <h1> Loading.... </h1>}
    {error && <p>ERROR </p>}
    {console.log(data)}
    <img
      src={`${Server}/productImages/${data[0]?.productImage}`}
      alt={`${data[0]?.productName}`}
    />
    <div className="data">
      <h1>{data[0]?.productName}</h1>
      <h3>{data[0]?.productPrice}</h3>
    </div>
  </Info>
</Wrapper>

It also seems that you are really expecting data to be an array, so you will want your initial state to maintain a state/type invariant, so it should also be declared as an array.

const [data, setData] = useState([]);
  • Related