React+TypeScript中onChange无值及event.preventDefault报错修复
问题描述
我在实现表单验证(useForm、yupResolver和Yup)之前,handleChange函数运行正常。现在console.log(value)无输出,输入框内容似乎没被记录,还抛出以下错误:
Uncaught (in promise) TypeError: event.preventDefault is not a function
at executeSearch (index.tsx:64:1)
at createFormControl.ts:1045:1
我是React和TypeScript新手,请问该怎么修复?
原代码
import React, { useState, useEffect, FormEventHandler } from "react"; import { useForm } from "react-hook-form"; import { yupResolver } from "@hookform/resolvers/yup"; import Loader from "react-ts-loaders"; import { WeatherApiVariables } from "../../../variables"; import { inputSchema } from "../../../validations/schema"; import lowTemperature from "../../../assets/icons/low-temperature-icon.svg"; import highTemperature from "../../../assets/icons/high-temperature-icon.svg"; import styles from "./weather-api.module.scss"; export function WeatherApi() { const [cityName, setCityName] = useState<string>(""); const [cityWeather, setCityWeather]: any = useState<{}>({}); const [cityWeatherForecast, setCityWeatherForecast]: any = useState<[]>([]); const [value, setValue] = useState(""); const [loading, setLoading] = useState<boolean>(false); const { register, handleSubmit, formState: { errors }, }: any = useForm({ resolver: yupResolver(inputSchema), }); async function getCurrentWeather(cityName: string): Promise<any> { setLoading(true); const response = await fetch( `https://api.weatherapi.com/v1/current.json?key=${WeatherApiVariables.token}&q=${cityName}&${WeatherApiVariables.airQuality}&${WeatherApiVariables.dataLanguage}` ); const data = await response.json(); return data; } async function getWeatherForecast(cityName: string): Promise<any> { setLoading(true); const response = await fetch( `https://api.weatherapi.com/v1/forecast.json?key=${WeatherApiVariables.token}&q=${cityName}&${WeatherApiVariables.airQuality}&${WeatherApiVariables.dataLanguage}&days=${WeatherApiVariables.forecastDays}` ); const data = await response.json(); return data; } useEffect(() => { async function fetchData() { const currentWeather = await getCurrentWeather(cityName); const weatherForecast = await getWeatherForecast(cityName); setCityWeather(currentWeather); setCityWeatherForecast(weatherForecast); setLoading(false); } fetchData(); }, [cityName]); const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { setValue(event.target.value); console.log(value); }; const executeSearch: FormEventHandler<HTMLFormElement> = ( event: React.FormEvent<HTMLFormElement> ) => { event.preventDefault(); const transformedText = value .normalize("NFD") .replace(/[\u0300-\u036f]/g, ""); setCityName(transformedText); setValue(""); }; return ( <section className={styles.main}> <div className={styles.search_container}> <h2>Busque por uma cidade</h2> <form onSubmit={handleSubmit(executeSearch)} className={styles.search_form} > <input type="text" name="searchField" onChange={handleChange} placeholder="Ex: Blumenau, Santa Catarina" {...register("searchField")} /> <p>{errors.searchField?.message}</p> <input type="submit" className={styles.search_btn} value="Pesquisar" /> </form> </div> <div className={styles.current_weather_section}> {loading ? ( <Loader className="loader" type="roller" size={150} /> ) : ( <> {!cityWeather.location ? ( "" ) : ( <div className={styles.current_weather_container}> <h2> Condição climática atual em:</h2> <h3> {cityWeather.location.name}, {cityWeather.location.region} </h3> <div className={styles.condition_container}> <span>{cityWeather.current.condition.text}</span> <img alt="ícone da condição atual do tempo" src={cityWeather.current.condition.icon} ></img> </div> <span>Temperatura atual: {cityWeather.current.temp_c} °C</span> <span> Sensação térmica: {cityWeather.current.feelslike_c} °C </span> </div> )} </> )} </div> {!cityWeatherForecast.forecast ? ( "" ) : ( <> <h2>Previsão para os próximos dias</h2> <section className={styles.weather_forecast_section}> {cityWeatherForecast.forecast.forecastday.map( (item: any, key: string) => { const dateUnix = (item.date_epoch + 86400) * 1000; const newDate = new Date(dateUnix); const forecastWeekDay = newDate.toLocaleString("pt-BR", { weekday: "long", }); const forecastDay = newDate.toLocaleString("pt-BR", { day: "numeric", }); return ( <ul key={key} className={styles.weather_forecast_list}> <li> <h2> {forecastWeekDay} - {forecastDay} </h2> <div className={styles.forecast_condition_container}> <span>{item.day.condition.text}</span> <img alt="ícone do clima" src={item.day.condition.icon} /> </div> <p> Poss. chuva: {item.day.daily_chance_of_rain} % -{" "} {item.day.totalprecip_mm} mm </p> <p> <img src={lowTemperature} alt="termômetro - temperatura baixa" /> {item.day.mintemp_c} °C </p> <p> <img src={highTemperature} alt="termômetro - temperatura alta" /> {item.day.maxtemp_c} °C </p> </li> </ul> ); } )} </section> </> )} </section> ); }
修复方案
1. 修正executeSearch参数错误
react-hook-form的handleSubmit会把表单数据传给回调函数,而非事件对象。你之前写的event.preventDefault()自然会报错,而且handleSubmit已经自动阻止了表单默认提交行为,不需要手动调用。
修改executeSearch:
// 定义表单数据类型,替代any type FormInputs = { searchField: string; }; const executeSearch = (data: FormInputs) => { const transformedText = data.searchField .normalize("NFD") .replace(/[\u0300-\u036f]/g, ""); setCityName(transformedText); };
2. 移除自定义状态和事件处理
react-hook-form会自动管理输入框状态,你手动写的value和handleChange会和register生成的逻辑冲突,导致输入不更新。
删除以下代码:
const [value, setValue] = useState(""); // ... const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { setValue(event.target.value); console.log(value); };
同时修改input组件,去掉onChange={handleChange}:
<input type="text" name="searchField" placeholder="Ex: Blumenau, Santa Catarina" {...register("searchField")} />
3. 优化useForm类型定义
给useForm指定类型,避免any:
const { register, handleSubmit, formState: { errors }, } = useForm<FormInputs>({ resolver: yupResolver(inputSchema), });
4. 避免空字符串触发请求(可选)
当前组件挂载时,cityName是空字符串,会直接发起无效请求。可以在useEffect里加判断:
useEffect(() => { async function fetchData() { if (!cityName) return; // 空字符串不请求 setLoading(true); try { const currentWeather = await getCurrentWeather(cityName); const weatherForecast = await getWeatherForecast(cityName); setCityWeather(currentWeather); setCityWeatherForecast(weatherForecast); } catch (err) { console.error("请求天气数据失败:", err); } finally { setLoading(false); } } fetchData(); }, [cityName]);
内容的提问来源于stack exchange,提问作者Alex_Lima84
相关产品推荐
相关产品推荐

