Reputation: 140
I have the code below and before the validation implementation (useForm, yupResolver and Yup), the handleChange function was working just fine. If I console.log the value variable, I get nothing. It seems that it is not registering what I'm typing in the input field. Besides, I get the error below on my browser:
Uncaught (in promise) TypeError: event.preventDefault is not a function
at executeSearch (index.tsx:64:1)
at createFormControl.ts:1045:1
How can I fix this? Sorry, I'm relatively new with TypeScript and React.
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>
);
}
Upvotes: 0
Views: 246
Reputation: 140
I found out how to do it correctly:
import { useState, useEffect } 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";
interface ISearch {
searchField: string;
}
export function WeatherApi() {
const [cityName, setCityName] = useState<string>("");
const [cityWeather, setCityWeather]: any = useState<{}>({});
const [cityWeatherForecast, setCityWeatherForecast]: any = useState<[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const {
register,
resetField,
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 handleSearch = (data: ISearch) => {
const transformedText = data.searchField
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "");
setCityName(transformedText);
resetField("searchField");
};
return (
<section className={styles.main}>
<div className={styles.search_container}>
<h2>Busque por uma cidade</h2>
<form
onSubmit={handleSubmit(handleSearch)}
className={styles.search_form}
>
<input
type="text"
name="searchField"
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>
);
}
Upvotes: 0
Reputation: 3505
The error is happening on your executeSearch
function and is because you're using that function in conjunction with the useForm
prop handleSubmit
if you look the documentation the handleSubmit
function accepts a another function that has three arguments data (which is the form structure shape you want to give your form) the errors (which has an object containing the incorrect form items) and the event Object (which has all the event information):
((data: Object, e?: Event) => void, (errors: Object, e?: Event) => void) => Function
So that means that what your executeSearch
function is using the data object as the event, so it is not the actual event itself, is the formData
. You probably don't need the event.preventDefault()
because react hook form handles that for you, in case you want to use the event for something else you must use the right parameter like this:
const executeSearch = (
_data,
_errors,
event
) => {
event.preventDefault();
const transformedText = value
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "");
setCityName(transformedText);
setValue("");
};
I highly suggest to read the TS documentation for react hook form, once you get used to it, you won't want to do the forms yourself again.
https://react-hook-form.com/ts
Also is this is very confusing for you, I also suggest to read the TS docs and have a better understanding of types and interfaces, so you can use react hook form as it is intended and use your own interfaces and types in conjunction with it.
https://www.typescriptlang.org/docs/handbook/typescript-tooling-in-5-minutes.html#interfaces
Upvotes: 1