skip to Main Content

Basically, I have a search page component which displays the search results of the query that the user typed in the input field.

This is the file.

"use client";

import { useSearchParams } from "next/navigation";
import useFetch from "../hooks/useFetch";
import ProductCard from "@/components/ProductCard";

const SearchPage = () => {
  const search = useSearchParams();
  const searchQuery = search ? search?.get("q") : null;
  const encodedSearchQuery = encodeURI(searchQuery || "");

  const { data } = useFetch(
    `/products?populate*=&filters[title][$contains]=${encodedSearchQuery}`
  );
  console.log(data);

  return (
    <div>
      <div>
         {/* {data?.map((product) => (
          <ProductCard key={product.id} products={product} />
        ))} */}
      </div>
    </div>
  );
};

export default SearchPage;

I am using a global hook useFetch for it.
I am able to see the filtered data in the console as well.
Productcard component is basically for structure and it takes a prop called products

I tried mapping with the commented out code but it shows me these errors.

This is useFetch.tsx

In this snippet, I have a utility function called fetchData that is responsible for making API requests. It uses Axios to make the request and includes an authorization header.

"use client";

import { useState, useEffect } from "react";
import { fetchData } from "@/app/utils/api";

const useFetch = (endpoint: string) => {
  const [data, setData] = useState(null);

  useEffect(() => {
    const fetchDataFromApi = async () => {
      try {
        const res = await fetchData(endpoint);
        setData(res);
      } catch (error) {
        console.error("Error fetching data:", error);
      }
    };

    fetchDataFromApi();
  }, [endpoint]);

  return { data };
};

export default useFetch;

Property ‘map’ does not exist on type ‘never’.
Parameter ‘product’ implicitly has an ‘any’ type.

I actually saw someone do it exactly the same way but with Javascript.
I am using typescript, I am new to it. How should I do it with typescript?

I can show more components related to it if someone wants to see that.

Any help is appreciated

2

Answers


  1. You need to declare and pass the expected type (e.g: Product) to TypeScript and make the useFetch generic (taking in the type of expected data returned):

    // types.ts
    export interface Product {
      // replace with the actual data structure
      id: string
      name: string
      description: string
      price: number
      // etc...
    }
    
    // useFetch.ts
    export function useFetch<T>(endpoint: string): { data: T[] } {
      const [data, setData] = useState<T[]>([]);
    
      // ... actual fetching and setting to `data`
    
      return { data }
    }
    
    // in any component:
    import { Product } from './path/to/types'
    import { useFetch } from './path/to/useFetch'
    
    const { data } = useFetch<Product>(
      `/products?populate*=&filters[title][$contains]=${encodedSearchQuery}`
    )
    

    Now TypeScript knows data‘s type (Product[]).

    Notes:

    • Product can also be a type.
    • you might want to also return error and loading from useFetch, which would allow you to handle the error and loading states in your components, but that’s outside the scope of this question
    Login or Signup to reply.
  2. So the issue you’re having is connected with TypeScript part of it. When fetching the data I believe you are getting back an any from the fetchData and the type your res is any.

    But the core issue is your useState declaration. You’re initializing it to null, so when you try to access data?.anyMethod() it is going to be never, and it will gives out "Property ‘map’ does not exist on type ‘never’".

    You can use Generics with useState (which itself is a generic function) to let TypeScript know what kind of data it is going to store, in your case you can create a type for your product:

    interface Product {
     id: number;
     name: string;
     ...
    }
    

    and hand it over to useState generic in your case as I guess you’re expecting an array of products, it should be:

     const [data, setData] = useState<Product[] | null>(null);
    
      
      useEffect(() => {
      ...
    

    But as your useFetch function itself is reusable one I’d advice you to make it a generic one itself, so you can pass the type when calling it and expect to receive it. You can achieve this doing so:

    const useFetch = <T,>(endpoint: string) => {
      const [data, setData] = useState<T | null>(null);
      ...
    }
    
    

    And after when calling it you just pass the type:

    const { data } = useFetch<Product[]>(
        `/products?populate*=&filters[title][$contains]=${encodedSearchQuery}`
      );
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search