You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React TextField输入值变更时立即重渲染未等待表单提交问题

问题根因

异常由三个代码错误直接导致:

  • 状态耦合:把输入框实时输入的临时内容和触发API请求的城市查询参数共用了父组件的city状态,而App.js中配置了useEffect监听city变化,只要city被修改就会立刻发起接口请求。
  • 事件绑定错误:将父组件的setCity方法直接绑定到输入框的变更事件(代码中误写为onSubmit,实际触发时机和输入变更一致),导致用户每输入一个字符都会直接修改全局city状态。
  • 表单标签错误:用普通div作为输入框和提交按钮的容器,div不支持原生submit事件,点击提交按钮无法触发预期的提交逻辑,提交流程完全失效。
修复方案
  1. 拆分状态:在Daily组件内部新增独立的inputCity状态,专门存储输入框的实时输入内容,不直接关联API请求逻辑。
  2. 替换表单容器:把包裹输入框、提交按钮的div换成原生form标签,支持回车/点击按钮触发提交事件。
  3. 修正事件绑定:给TextField绑定正确的onChange事件,输入时只更新组件内部的inputCity状态;仅在提交事件触发、校验输入内容合法后,才调用父组件传入的setCity更新查询参数,触发接口请求。
  4. 补全Hook依赖:修复useEffect、useCallback的缺失依赖,避免闭包导致的旧值问题。

修正后Daily组件代码

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('')
    // 单独维护输入框临时值,初始值和当前生效的城市保持一致
    const [inputCity, setInputCity] = useState(city)

    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()
        // 提交时校验非空,才更新全局城市状态触发请求
        if(inputCity.trim()){
          setCity(inputCity.trim())
        }
      }      

      useEffect(()=>{
        setTime(timeConverter(dt))
      },[dt])
  return (
    <>
            <h1 className='title'>Monkey Wit Da Weather</h1>
            <form className="textInput" onSubmit={handleSubmit}>
            <TextField id="outlined-basic" 
            placeholder='city'  
            name='city' 
            type="text" 
            onChange={(event)=>setInputCity(event.target.value)} 
            value={inputCity}
            InputProps={{
          startAdornment: (
            <InputAdornment position="start">
              <PinDropIcon/>
            </InputAdornment>
          ),
        }} variant="standard" />
        <button className="submit" type="submit"><FontAwesomeIcon icon={faArrowRight} /></button>    
        </form>
          
    </>
  )
}

export default Daily

修正后App.js代码

import './cssdirect/App.css';
import Daily from './components/daily';
import {useState, useEffect, useCallback} from 'react'
import Loading from './Loading';

function App() {
const [loading, setLoading] = useState(true)
const [city, setCity] = useState('Forest%20Hills')
const [weatherData, setWeather] = useState([])

const getWeather = useCallback(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')
  }
}, [city])

useEffect(()=>{
  getWeather()
},[getWeather])

  if(loading==true){
    return <Loading/>
  }
  else{
    return <Daily {...weatherData} setCity={setCity} city={city}/>
  }
}

export default App;
修复后逻辑说明
  • 输入过程中仅更新组件内部临时状态,不会触发父组件重渲染和API调用
  • 只有点击提交按钮、或输入框聚焦时按回车触发表单提交,才会更新全局城市参数拉取对应天气
  • 支持输入空值校验,避免无效请求

内容的提问来源于stack exchange,提问作者Het Patel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 08:45:16