React Hook "useForecast" is called in function "getSearch" that is neither a React function component nor a custom React Hook function

React Newbie Here!

I have been running into the same issue over and over again with a React weather dashboard project I have been working on. I am focusing on connecting the submit functions and the forecast function I have created to retrieve the json data from the OpenWeatherMap API I am using. Upon utilizing a callback function I have created (to grab the searched city value from the child component), the error that is listed above is thrown. As I have not intended the callback function 'getSearch' to be a function component or a custom React Hook, I have no clue what else to try to fix this error. Below is all of my code for my Parent component 'overview.js' and the child component 'recentSearches.js':

overview.js

import React, {useState} from 'react';
import './overview.css';
import { RecentSearches } from '../Recent Searches/recentSearches';
import { Hourly } from '../Hourly/hourly';
import { Fiveday } from '../5 Day Forecast/fiveday';

require('dotenv').config(); 

const useForecast = async (city) => {

    // const api_key = process.env.API_KEY;
    const api_key = '2220f5a5d83243938f764759211310';
    var BASE_URL = `http://api.weatherapi.com/v1/forecast.json?key=${api_key}&q=${city}&days=3&aqi=no&alerts=no`;

    const response = await fetch(BASE_URL);
    const data = await response.json();
    console.log(data);
    // collects all of the current weather info for your search
    const currentTempInfo = {
        city: data.location.name, 
        state: data.location.region, 
        epochDate: data.location.localtime_epoch, 
        message: data.current.condition.text, 
        wicon: data.current.condition.icon, 
        currentTemp: data.current.temp_f,
        currentHighTemp: data.forecast.forecastday[0].day.maxtemp_f,
        currentLowTemp: data.forecast.forecastday[0].day.mintemp_f,
        feelsLike: data.current.feelslike_f,
        humidity: data.current.humidity,
        rainLevel: data.current.precip_in,
        // hourlyTemps is an array, starts from midnight(index 0) and goes every hour until 11 pm(index 23)
        hourlyTemps: data.forecast.forecastday[0].hour.map((entry) => {
            let epochTime, temp;
            [epochTime, temp] = [entry.time_epoch, entry.temp_f];
            return [epochTime, temp];
        })
    };
    const daycardInfo = [];

    // this for loop triggers and creates an array with all necessary values for the 
    for (var x in data) {
        const fcDayDates = data.forecast.forecastday[x].date_epoch;
        const dayInfo = data.forecast.forecastday[x].day; 
        const dayValues = {
            dates: fcDayDates, 
            message: dayInfo.condition.text, 
            wicon: dayInfo.condition.icon, 
            maxTemp: dayInfo.maxtemp_f, 
            minTemp: dayInfo.mintemp_f, 
            avgTemp: dayInfo.avgtemp_f
        };
        // pushes dayValues object into daycardInfor array
        daycardInfo.push(dayValues);    
    };

    // this updates the state with the forecast for the city entered
    data = {
        currentTempInfo: currentTempInfo,
        daycardInfo: daycardInfo
    };

    // this spits out the newly created forecast object
    return data;
};

export function Overview() {
    const [search, setSearch] = useState(null);

    // 
    const getSearch = (searchedCity) => {
        setSearch(searchedCity);
        useForecast(search);
    };

    return (
        <div>
            <div class='jumbotron' id='heading-title'>
                <h1>Welcome to <strong>Weathered</strong>!</h1>
                <h3>A Simple Weather Dashboard </h3>
            </div>

            <div class='container-fluid' id='homepage-skeleton'>
                <div class='d-flex' id='center-page'>
                    <RecentSearches callback={callback}/>
        
                    <Hourly />
                </div>
            </div>

            <Fiveday />        
        </div>
    )
};

recentSearches.js

import React, {useState} from 'react';
import './recentSearches.css';

export function RecentSearches({getSearch}) {
    const [city, setCity] = useState('');
    const [recents, setRecents] = useState([]);

    const onSubmit = e => {
        e.preventDefault();
        
        if (!city || city === '') {
            return;
        } else {
            addRecent(city);
            getSearch(city);
        }  
    }
 
    const onChange = (e) => {
        setCity(e.target.value);
    }; 

    // function which takes in searched entry and adds entry to recent searches array
    function addRecent(newSearch) {
        if (recents.includes(newSearch)) {
            return;
        } else {
            setRecents((prev) => [newSearch, ...prev]);
        }
        // clear our search bar entry
        setCity('');
    }; 

    const searchAgain = (e) => {
        const recent = e.target.innerHTML;
        setCity(recent);
    }
    
    return (
        <section id='search-bar-recents'>
            <h3 class='m-3'>Recents </h3>
            <div id='insert-recent-buttons'>{recents.map((entry, index) => <h5 key={index} onClick={searchAgain} value={entry}>{entry}</h5>)}</div>

            <form onSubmit={onSubmit} class="m-3">
                <label className="form-label" for="search-bar">Search A City</label><br/>
                <input className='form-text' id="search-bar" type='text' value={city} placeholder="Las Vegas, etc." onChange={onChange}/>
                <input className='form-button' id='search-button' type='submit' value='Search'/>
            </form>
        </section>
    )
};

Any help/tips/notes are appreciated! Thanks!


Solution 1:

The rules-of-hooks lint plugin uses naming conventions to determine what functions are custom hooks. Since your function starts with use, the lint plugin assumes it is a hook and thus that it must follow the rules of hooks. But your function is not using any of react's built in hooks, nor is it calling any custom hooks, so it doesn't actually need to follow the rules of hooks.

The fix is simply to rename the function, so the lint plugin doesn't think it's a custom hook. Maybe fetchForecast.

const fetchForecast = async (city) => {
    // const api_key = process.env.API_KEY;
    const api_key = '2220f5a5d83243938f764759211310';
    var BASE_URL = `http://api.weatherapi.com/v1/forecast.json?key=${api_key}&q=${city}&days=3&aqi=no&alerts=no`;
    // ...
}

// Used like:
const getSearch = async (searchedCity) => {
  setSearch(searchedCity);
  const forecast = await fetchForecast(searchedCity);
};