Home > Net >  Issue with immediate rerendering when input value is change in text field
Issue with immediate rerendering when input value is change in text field

Time:06-19

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"
/>;

  • Related