Search code examples
reactjstypescriptfetch-apiuse-state

How to pass a value from an input to a submit button?


I'm currently working on a project to implement a website to check the weather forecast.

I'm trying to get the value from the input field and when I click the submit button, this value should be set to cityName. What do I have to change in order to make this work?

import { useState, useEffect } from "react"


export function WeatherInfo() {

    const token: string = '7ebe7c2a03cd48c090a193437'

    async function getCurrentWeather(cityName: string): Promise<any> {
        const response = await fetch(`http://api.weatherapi.com/v1/current.json?key=${token}&q=${cityName}`)
        const data = await response.json()
        console.log(data)
        return data
    }

    const [cityName, setCityName]: any = useState('')
    const [cityWeather, setCityWeather] = useState({})
    const [value, setValue] = useState('')

    const handleChange = (event: any) => {
        setValue(event.target.value)
    }

    const handleSubmit = (event: any) => {
        event.preventDefault()
        setCityName(value)
    }

    useEffect(() => {
        async function fetchData() {
            const cityWeather = await getCurrentWeather(cityName)

        }
        fetchData()
    })


    return (
        <div >
            <form onSubmit={handleSubmit}>
                <input onChange={handleChange} placeholder="Type here" />
                <button>Search</button>
            </form>

        </div>
    );

}

Solution

  • You should add a dependency array to your effect hook so that it triggers whenever cityName changes.

    Updating the cityWeather state should only be done via the setCityWeather function.

    useEffect(() => {
      if (cityName) { // only fetch when you've got a value
        getCurrentWeather(cityName).then(setCityWeather);
      }
    }, [cityName]);
    

    You should also try to use as few any types as possible, preferably none

    // define stand-alone functions outside your components
    // eg weather-api.ts
    const token = "your-api-key";
    
    export interface CurrentWeather {
      temp_c: number;
      feelslike_c: number;
      // etc
    }
    
    export async function getCurrentWeather(
      cityName: string
    ): Promise<CurrentWeather> {
      // safely encode URL query params
      const params = new URLSearchParams({
        key: token,
        q: cityName,
      });
    
      const response = await fetch(
        `http://api.weatherapi.com/v1/current.json?${params}`
      );
    
      // don't forget to check for errors
      if (!response.ok) {
        throw response;
      }
    
      return response.json(); // will be cast to the `CurrentWeather` type
    }
    
    import { useState, useEffect, FormEventHandler } from "react";
    import { getCurrentWeather, CurrentWeather } from "./weather-api";
    
    export function WeatherInfo() {
      const [cityName, setCityName] = useState("");
      const [cityWeather, setCityWeather] = useState<CurrentWeather>(); // default undefined
      const [value, setValue] = useState("");
    
      useEffect(() => {
        getCurrentWeather(cityName).then(setCityWeather).catch(console.error);
      }, [cityName]);
    
      const handleSubmit: FormEventHandler<HTMLFormElement> = (event) => {
        event.preventDefault();
        setCityName(value);
      };
    
      return (
        <div>
          {cityWeather && (
            <p>
              The current temperature in {cityName} is {cityWeather.temp_c} &deg;C
            </p>
          )}
          <form onSubmit={handleSubmit}>
            <input
              onChange={(e) => setValue(e.target.value)}
              placeholder="Type here"
            />
            <button>Search</button>
          </form>
        </div>
      );
    }