Alex_Lima84
Alex_Lima84

Reputation: 140

How do I fix my onChange event to get the event.target.value?

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

Answers (2)

Alex_Lima84
Alex_Lima84

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

jean182
jean182

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

Related Questions