Search code examples
reactjsreact-hooksasync-awaitfetch-apitanstackreact-query

Hooks are not supported inside an async component error in nextjs project using useQuery


I've been having trouble with how I'm supposed to fetch the data using an API call. I'm using useQuery and I've looked around for how I'm supposed to implement it and I'm just getting errors with it and async keywords that may or may not be needed in the file. I'm just confused with use client and what should and shouldn't be in that kind of file.

Here's how I have useQuery set up


"use client"

import { useQuery } from "@tanstack/react-query";
import fetchData from "../app/fetchData.jsx";  

export default function Temps(){
  
    const{  data, error, isError } = useQuery({
         queryKey: ["wdata"], 
         queryFn:  () =>  fetchData(),
        
    });   
      
      if (isError) <span>Error: {error.message}</span>
      if(data)
      return(
        <div>
        <p> {data.toString()}</p>
        </div>
      )
  
    }

Here's where I have fetchData


"use client"

import { usePathname } from "next/navigation";


async function getData(){
  const options = {
    method:"GET",
    headers: {
      accept: "application/json",
    },
  };

  
    const pathName = usePathname();

    const { searchParams } = pathName;
    const lat = searchParams.get('lat');
    const lon = searchParams.get('lon');


    const response = await fetch(
        `http://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${process.env.API_KEY}&units=imperial`,
        options)
          .then ((response) => response.json())
          .catch((err) => console.error(err));

    return response;

}

export default async function fetchData(){
  

const data = await getData();
return data;

}

I'm thinking there might be something in the above code that's causing the error. Maybe the usePathname and searchParams but I'm not exactly sure how I should solve it.

also here's the home page where I use useQueryClient

import WeatherApp from './WeatherApp';
import { QueryClient } from '@tanstack/react-query';
import fetchData from './fetchData';
import Temps from '@/components/Temps';

export default  async function Home() {
  const queryClient = new QueryClient()
await queryClient.prefetchQuery({
  queryKey: ["wdata"],
  queryFn: () =>  fetchData(),
})
  
  return (
    <div>
    <WeatherApp />
    <Temps />
    </div>
  );
}


Solution

  • You can't call a React hook in a nested callback, i.e. calling usePathname in getData is not valid.

    You can call usePathname in the Temps component and pass the lat, and lon values to the query function.

    Basic Example:

    "use client"
    
    import { useQuery } from "@tanstack/react-query";
    import { usePathname } from "next/navigation";
    import fetchData from "../app/fetchData.jsx";  
    
    export default function Temps() {
      const { searchParams } = usePathname();
    
      const lat = searchParams.get('lat');
      const lon = searchParams.get('lon');
    
      const { data, error, isError } = useQuery({
        queryKey: ["wdata", lat, lon], 
        queryFn: () =>  fetchData({ lat, lon }),
      });   
          
      if (isError) <span>Error: {error.message}</span>;
    
      if (data)
        return (
          <div>
            <p>{JSON.stringify(data)}</p>
          </div>
        );
      }
    }
    
    "use client"
    
    import { usePathname } from "next/navigation";
    
    async function getData({ lat, lon }) {
      const options = {
        method:"GET",
        headers: {
          accept: "application/json",
        },
      };
    
      try {
        const response = await fetch(
          `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${process.env.API_KEY}&units=imperial`,
           options
        )
        const data = await response.json();
        return data;
      } catch(err) {
        console.error(err);
        throw err; // re-throw for query
      };
    }
    
    export default function fetchData({ lat, lon }) {
      return getData({ lat, lon });
    }
    

    Additional Issues Addressed in Sandbox Code

    1. The OpenWeatherMap APIs expect you to use the secure https protocol, the request URL should be:

      `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${process.env.NEXT_PUBLIC_API_KEY}&units=imperial`
      
    2. The fetchData utility function incorrectly console logged a property of an undefined object, likely throwing an exception that was being silently caught by the useQuery hook.

      export default function fetchData({ lat, lon }){
        // only lat lon log displays
        console.log("fetchData lat and lon " + lat + ", " + lon); // OK
        // console.log("fetchData", data.data) // Not OK, data is undefined, data.data throws
        return getData({ lat, lon });
      }
      
    3. The endpoint returns data similar to the following:

      {
          "coord": {
              "lon": 43,
              "lat": 23
          },
          "weather": [
              {
                  "id": 800,
                  "main": "Clear",
                  "description": "clear sky",
                  "icon": "01d"
              }
          ],
          "base": "stations",
          "main": {
              "temp": 81.95,
              "feels_like": 79.79,
              "temp_min": 81.95,
              "temp_max": 81.95,
              "pressure": 1009,
              "humidity": 19,
              "sea_level": 1009,
              "grnd_level": 898
          },
          "visibility": 10000,
          "wind": {
              "speed": 3.53,
              "deg": 307,
              "gust": 5.82
          },
          "clouds": {
              "all": 1
          },
          "dt": 1713591517,
          "sys": {
              "country": "SA",
              "sunrise": 1713580975,
              "sunset": 1713627024
          },
          "timezone": 10800,
          "id": 110250,
          "name": "Afif",
          "cod": 200
      }
      

      This is the data value returned by the useQuery hook that Temps can use. Select what you need from this object, e.g. data?.main.temp is 81.95.

      enter image description here