My application relies on the 'city' value to call an API and receive databack, for that, I set up a form made it use a setState value on my city, and a useEffect value on my main App.js so every time a new city was submitted through the input, it would rerender. The issue is that the setCity function goes off immediately as soon as the text inside the input is changed and does not wait for the submit function. Any help ?
import * as React from 'react';
import '../cssdirect/App.css'
import Icon from './Icon'
import Wind from './Wind'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faDroplet } from '@fortawesome/free-solid-svg-icons'
import { faArrowRight } from '@fortawesome/free-solid-svg-icons'
import TextField from '@mui/material/TextField';
import InputAdornment from '@mui/material/InputAdornment';
import PinDropIcon from '@mui/icons-material/PinDrop';
import { useState, useEffect } from 'react';
const Daily = ({coord, weather, main, visibility, wind, clouds, sys, timezone, name, dt, setCity, city}) => {
const [time, setTime] = useState('')
function timeConverter(dt){
var a = new Date(dt * 1000);
var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
var year = a.getFullYear();
var month = months[a.getMonth()];
var date = a.getDate();
var hour = a.getHours();
var time = date ' ' month ' ' year ' ' hour;
return time;
}
const handleSubmit = (e) =>{
e.preventDefault()
console.log(city)
}
useEffect(()=>{
setTime(timeConverter(dt))
},[])
return (
<>
<h1 className='title'>Monkey Wit Da Weather</h1>
<div className="textInput"
onSubmit={handleSubmit}>
<TextField id="outlined-basic"
placeholder='city'
name='city'
type="text"
onSubmit={(event)=>setCity(event.target.value)}
value={city}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<PinDropIcon/>
</InputAdornment>
),
}} variant="standard" />
<button className="submit" onClick={handleSubmit} type="submit"><FontAwesomeIcon icon={faArrowRight} /></button>
</div>
</>
)
}
export default Daily
App.js
import './cssdirect/App.css';
import Daily from './components/daily';
import {useState, useEffect} from 'react'
import Loading from './Loading';
function App() {
const [loading, setLoading] = useState(true)
const [city, setCity] = useState('Forest Hills')
const [weatherData, setWeather] = useState([])
const getWeather = async() =>{
setLoading(true)
try{
const resp = await fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=f8d957da06af592a1390c360ea801908&units=imperial`)
const pResp = await resp.json()
setWeather(pResp)
setLoading(false)
}
catch{
console.log('error')
}
}
useEffect(()=>{
getWeather()
},[city])
if(loading==true){
return <Loading/>
}
else{
return <Daily {...weatherData} setCity={setCity} city={city}/>
}
}
export default App;
CodePudding user response:
Yes, it should be the behavior the way you coded, because you're using an useEffect
with city
as the dependency on it to trigger getWeather
async function (check the below code in App.js
). This trigger happens whenever city
state changes during re-rendering.
useEffect(()=>{
getWeather()
},[city])
What you need is to trigger the getWeather
async function when you do the submission, not when you change city
. Therefore, pass getWeather
async function to child component (Daily
) as a prop and call it inside the handleSubmit
as follows.
const handleSubmit = (e) =>{
e.preventDefault()
console.log(city)
getWeather()
}
And additionally, remove the previously mentioned useEffect
as well.
By the way, I think it's better if you use onChange
event instead of onSubmit
inside TextField
component from material UI. (reference)
<TextField
id="outlined-basic"
placeholder="city"
name="city"
type="text"
onChange={(event) => setCity(event.target.value)}
value={city}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<PinDropIcon />
</InputAdornment>
),
}}
variant="standard"
/>;