0

I'm sure I'm just doing something stupid, but I can't figure out what I'm doing wrong.

When the user fills out their state and city and hits submit, that should run a fetch request for lat and lon values, then that needs to be fed into another API that gives the weather based on lat and lon. I'm using react.js.

import {React, useState} from 'react'

export default function Home () {
    // // GeoApify gets Lat/Long
    // const geo = fetch(`https://api.geoapify.com/v1/geocode/search?city=${details.city}&state=${details.state}&format=json&apiKey=27a84d7b0c1b4d52b41acc3e82bbe239`)
    // // OpenWeatherApi One Call API 3.0 gets the weather of the Lat/Long
    // const weather = fetch(`https://api.openweathermap.org/data/3.0/onecall?lat=${coord.lat}&lon=${coord.lon}&appid=544ce8f74a895e6f7bd6425293b01b47`)

    const [coord, setCoord] = useState({lat:'', lon:''})
    const [details, setDetails] = useState({city:'', state:''})

    const fetch = (req, res) => {
        fetch(`https://api.geoapify.com/v1/geocode/search?city=${details.city}&state=${details.state}&format=json&apiKey=27a84d7b0c1b4d52b41acc3e82bbe239`)
        .then((res) => res.json())
        .then(fetch(`https://api.openweathermap.org/data/3.0/onecall?lat=${res.results.lat}&lon=${res.results.lon}&appid=544ce8f74a895e6f7bd6425293b01b47`))
        .then((res) => res.json())
        .then(weather.push(res))
    }
    const weather = []
    return(
        <div>
            <h4>Home</h4>
            <form id='form'>
                <p><label htmlFor='city'>City: </label>
                <input placeholder='City' type='text' name='city' onChange={e => setDetails({...details, city:e.target.value}, console.log(details))} value={details.city} /></p>
                <p><label htmlFor='state'>State: </label>
                <input placeholder='State' type='text' name='state' onChange={e => setDetails({...details, state:e.target.value}, console.log(details))} value={details.state} /> </p>
                <p><input type='submit' value='Submit' onSubmit={fetch()} /></p>
            </form>
        </div>
    )
}

I keep getting an error that says:

"Uncaught RangeError: Maximum call stack size exceeded".

Why does it seem to be looping? It also seems to be calling the "fetch" function before the form is submitted.

For reference this is what a call from the lat/lon API looks like:

{
"results": [
{
"datasource": {},
"country": "United States",
"country_code": "us",
"state": "Georgia",
"county": "Gwinnett County",
"city": "Snellville",
"town": "Snellville",
"lon": -84.0199108,
"lat": 33.857328,
"state_code": "GA",
"distance": 0,
"formatted": "Snellville, GA, United States of America",
"address_line1": "Snellville, GA",
"address_line2": "United States of America",
"category": "administrative",
"timezone": {
"name": "America/New_York",
"offset_STD": "-05:00",
"offset_STD_seconds": -18000,
"offset_DST": "-04:00",
"offset_DST_seconds": -14400,
"abbreviation_STD": "EST",
"abbreviation_DST": "EDT"
},
"result_type": "city",
"rank": {
"importance": 0.5276231202788444,
"popularity": 3.3564440571456937,
"confidence": 1,
"confidence_city_level": 1,
"match_type": "full_match"
},
"place_id": "5195b5f237460155c059f9f884ecbced4040f00101f90127d3010000000000c00208",
"bbox": {
"lon1": -84.042913,
"lat1": 33.818392,
"lon2": -83.950932,
"lat2": 33.89217
}
}
],
"query": {}
}

I just need the lat/lon from it.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129

3 Answers3

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

export default function Home() {
  const [coord, setCoord] = useState({ lat: "", lon: "" });
  const [details, setDetails] = useState({ city: "", state: "" });

  const fetch = (req, res) => {
    fetch(
      `https://api.geoapify.com/v1/geocode/search?city=${details.city}&state=${details.state}&format=json&apiKey=27a84d7b0c1b4d52b41acc3e82bbe239`
    )
      .then((res) => res.json())
      .then(
        fetch(
          `https://api.openweathermap.org/data/3.0/onecall?lat=${res.results.lat}&lon=${res.results.lon}&appid=544ce8f74a895e6f7bd6425293b01b47`
        )
      )
      .then((res) => res.json())
      .then((data) => {
        const { lat, lon } = data.results[0];
        setCoord({ lat, lon });
      });
  };
  return (
    <div>
      <h4>Home</h4>
      <form id="form">
        <p>
          <label htmlFor="city">City: </label>
          <input
            placeholder="City"
            type="text"
            name="city"
            onChange={(e) =>
              setDetails(
                { ...details, city: e.target.value },
                console.log(details)
              )
            }
            value={details.city}
          />
        </p>
        <p>
          <label htmlFor="state">State: </label>
          <input
            placeholder="State"
            type="text"
            name="state"
            onChange={(e) =>
              setDetails(
                { ...details, state: e.target.value },
                console.log(details)
              )
            }
            value={details.state}
          />{" "}
        </p>
        <p>
          <input type="submit" value="Submit" onSubmit={fetch()} />
        </p>
      </form>
    </div>
  );
}
Konrad
  • 21,590
  • 4
  • 28
  • 64
0

Well... Frist of all, your code form is a little bit messy, you should declare an async function to handle the submit request. Then you should have something like this:

// I renamed fetch to fetchFunction because fetch is an already function in node18 or browser
const fetchFunction = async () => {
    // this is your first request and from this u shoul parse your data
    try {
        const response = await(await fetch(`https://api.geoapify.com/v1/geocode/search?city=${details.city}&state=${details.state}&format=json&apiKey=27a84d7b0c1b4d52b41acc3e82bbe239`)).json();

        // and here you should do your second fetch in the same maner
    } catch (error) {
        console.log(error)
    }
}

That error is popping out because you have a memory leak here:

<input type='submit' value='Submit' onSubmit={fetch()} />

and the right form for this is (or "onSubmit={fetchFunction}" if you take my approach):

<input type='submit' value='Submit' onSubmit={fetch} />
CtrlSMDFK
  • 91
  • 1
  • 4
  • so when i do the second request it should use response.results.lat? (the parent value for the response is results) –  Dec 31 '22 at 20:29
  • now it looks like it may be trying to run the "fetchFunction", but the page reloads on submit before it actually does anything. i tried putting "event.preventDefault" in the "fetchFunction" and in an "onClick" event on the submit button that reads "onCLick={event.preventDefault}" but i keep getting "event is not defined". –  Dec 31 '22 at 22:08
  • Its normal, onSubmit throw an evet objecta as a function paramter to an callback, to prevent default behaviour you need to call event.preventDefault like this onSubmit={event => fetchFunction} – CtrlSMDFK Dec 31 '22 at 23:42
0

i took your code and made some changes, now it's functional.

import React from "https://cdn.skypack.dev/react@17.0.1";
import ReactDOM from "https://cdn.skypack.dev/react-dom@17.0.1";

function App() {
    const [coord, setCoord] = React.useState({ lat: "", lon: "" });
    const [details, setDetails] = React.useState({ city: "", state: "" });
    const fetchFunction = async (event) => {
//         here you should have your preventDefault function, as I said, you receive an event object as paramater in callback function when onSubmit is fired
        event.preventDefault();
        // this is your first request and from this u should parse your data
        try {
            const response = await (await fetch(
                `https://api.geoapify.com/v1/geocode/search?city=${details.city}&state=${details.state}&format=json&                        apiKey=27a84d7b0c1b4d52b41acc3e82bbe239`
            )).json();

            console.log('res', response);
            // and here you should do your second fetch in the same maner
        } catch (error) {
            console.log('eerr', error);
        }
    };

    const weather = [];
    return (
        <div>
            <h4>Home</h4>
            {/* I moved your callfunction on form because here it was refreshing your page */}
            <form id="form" onSubmit={fetchFunction}>
                <p>
                    <label htmlFor="city">City: </label>
                    <input
                        placeholder="City"
                        type="text"
                        name="city"
                        {/* here I made a little change, it was correct and functional but i wannted to be the best practice here, setState can take as parameter a new value or a callback with his previous value as paramater  */}
                        onChange={(e) =>
                            setDetails((previousValue) => ({
                                ...previousValue,
                                city: e.target.value
                            }))
                        }
                        value={details.city}
                    />
                </p>
                <p>
                    <label htmlFor="state">State: </label>
                    <input
                        placeholder="State"
                        type="text"
                        name="state"
                        onChange={(e) =>
                            setDetails((previousValue) => ({
                                ...previousValue,
                                state: e.target.value
                            }))
                        }
                        value={details.state}
                    />
                </p>
                <p>
                    <input type="submit" value="Submit" />
                </p>
            </form>
        </div>
     );
}

ReactDOM.render(<App />, document.getElementById("root"));
CtrlSMDFK
  • 91
  • 1
  • 4
  • In a form element, an input submit type will fire onSubmit event so that's why i moved the function. In the original form u basically run the function on input click event after that the onSubmit event was fired – CtrlSMDFK Jan 01 '23 at 16:01