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

