1

I'm working with react and a weather API and have put together this form:

function Forecast() {
  const [cities, setCities] = React.useState([]);

  const addCity = (city) => {
    if (!city.text || /^\s*$/.test(city.text)) {
      return;
    }

    const newCities = [city, ...cities];

    setCities(newCities);
  };

  const removeCity = (id) => {
    const removedArray = [...cities].filter((city) => city.id !== id);

    setCities(removedArray);
  };

  return (
    <div>
      <Form onSubmit={addCity} />
    </div>
  );
}

const Form = (props) => {
  const api = {
    key: "39471307bd579e5ce6b1d89dc164dd77",
    base: "https://api.openweathermap.org/data/2.5/",
  };

  const [input, setInput] = React.useState("");
  const [weather, setWeather] = React.useState({});
  const [query, setQuery] = React.useState("");

  const handleChange = (e) => {
    setInput(e.target.value);
  };

  const handleSubmit = (e) => {
    e.preventDefault();

    fetch(`${api.base}weather?q=${input}&appid=${api.key}&units=metric`)
      .then((res) => res.json())
      .then((result) => setWeather(result));

    props.onSubmit({
      id: Math.floor(Math.random() * 10000),
      text: input,
      data: weather,
    });

    setInput("");
  };

  return (
    <form className="search-container" onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="Enter City"
        name="text"
        value={input}
        className="search"
        onChange={handleChange}
      />
      <button className="add-card-button">
        <i className="fas fa-plus-circle"></i>
      </button>
    </form>
  );
};

ReactDOM.render(<Forecast />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>

When I'm testing this, the first time you search for a city it's returning no data and the second time you search, it's using the input from the first search as so on. Does anyone know how can I get it so that the first search returns data immediately?

Janez Kuhar
  • 3,705
  • 4
  • 22
  • 45
Mary Timms
  • 13
  • 3

3 Answers3

0

fetch() is asynchronous. You have to set your weather data after the API call finishes.

Furthermore, you have to make sure to use the fresh result (calling setWeather(result) doesn't update weather immediately).

Edit handleSubmit to something like this:

  const handleSubmit = (e) => {
    e.preventDefault();

    fetch(`${api.base}weather?q=${input}&appid=${api.key}&units=metric`)
      .then((res) => res.json())
      .then((result) => {
        props.onSubmit({
          id: Math.floor(Math.random() * 10000),
          text: input,
          data: result,
        });
        setWeather(result);
        setInput("");
      });
  };

Resources:

Full code:

function Forecast() {
  const [cities, setCities] = React.useState([]);

  const addCity = (city) => {
    if (!city.text || /^\s*$/.test(city.text)) {
      return;
    }

    const newCities = [city, ...cities];
    console.log(newCities);
    setCities(newCities);
  };

  const removeCity = (id) => {
    const removedArray = [...cities].filter((city) => city.id !== id);

    setCities(removedArray);
  };

  return (
    <div>
      <Form onSubmit={addCity} />
    </div>
  );
}

const Form = (props) => {
  const api = {
    key: "39471307bd579e5ce6b1d89dc164dd77",
    base: "https://api.openweathermap.org/data/2.5/",
  };

  const [input, setInput] = React.useState("");
  const [weather, setWeather] = React.useState({});
  const [query, setQuery] = React.useState("");

  const handleChange = (e) => {
    setInput(e.target.value);
  };

  const handleSubmit = (e) => {
    e.preventDefault();

    fetch(`${api.base}weather?q=${input}&appid=${api.key}&units=metric`)
      .then((res) => res.json())
      .then((result) => {
        props.onSubmit({
          id: Math.floor(Math.random() * 10000),
          text: input,
          data: result,
        });
        setWeather(result);
        setInput("");
      });
  };

  return (
    <form className="search-container" onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="Enter City"
        name="text"
        value={input}
        className="search"
        onChange={handleChange}
      />
      <button className="add-card-button">
        <i className="fas fa-plus-circle"></i>
      </button>
    </form>
  );
};

ReactDOM.render(<Forecast />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
Janez Kuhar
  • 3,705
  • 4
  • 22
  • 45
-1

useRef hook works well with forms. assign a useRef hook variable to the form

remove !!

  const [input, setInput] = React.useState("");

replace

const input = useRef('')

input tag

 <input
    type="text"
    placeholder="Enter City"
    name="text"
    ref={input}
    className="search"
  />

submit function

  const handleSubmit = (e) => {
e.preventDefault();

fetch(`${api.base}weather?q=${input.current.value}&appid=${api.key}&units=metric`)
  .then((res) => res.json())
  .then((result) => setWeather(result));

props.onSubmit({
  id: Math.floor(Math.random() * 10000),
  text: input.current.value,
  data: weather,
});

input.current.value = '';

};

code salley
  • 307
  • 4
  • 5
  • You've just replaced a normal state variable with a ref which solves absolutely nothing. You haven't explained your approach. Your code blocks are not properly indented. – Janez Kuhar Jun 27 '21 at 21:07
-1

I cant code right now. But try putting the fetch inside a useEffect hook. And trigger the effect somehow after the submit. With a condicional inside or something. Maybe passing the state as a dependency of the useEffect.

Maybe tomorrow i can send a proper propose for your code

  • That's not the problem here. `handleSubmit()` (and `fetch()` in turn) **does** get called when the form is submitted. – Janez Kuhar Jun 27 '21 at 20:56