Y.S.
Y.S.

Reputation: 317

React useEffect repeats an action several times

I have a form with next fields:

  1. A drop-down list that allows to choose a branch + A city that is automatically selected according to the branch selected.
  2. A calendar component that allows to select a date + A details about the date that automatically displays, such as whether it is a work day or a holiday.
  3. Filleds with the temperature details that automatically displays, depending on the city of the branch and date selected. It updated whenever either of them changes, and both are validted.

The check if the first two fields are validted, getting the temperature from API, and update the temperature fields in temperature (using useState), are done in useEffect. The problem is that every time there is a change in the first two fields, the API call is made two or three times.

I deleted the <React.StrictMode> and it doesn't help.

import Calendar from "../components/Calendar";
import Languages from "../components/Languages/Languages.json";
import React, { useContext, useEffect, useRef, useState } from "react";
import { ContextCities } from "../Contexts/ContextCities";
import { ContextLanguages } from "../Contexts/ContextLanguages";

import { loadBranches } from "../utils/branchesHandelApi";
import { loadWeather } from "../utils/weatherHandelApi";

export default function Transactions() {
    const { cities } = useContext(ContextCities);
    const { selectedLanguage } = useContext(ContextLanguages);

    const [branch, setBranch] = useState([]);
    const [selectedBranchIndex, setSelectedBranchIndex] = useState(0);
    const [selectedDate, setSelectedDate] = useState();
    const [selectedDateData, setSelectedDateData] = useState();
    const [weather, setWeather] = useState(0);

    const handleSubmitTransaction = async (e) => {
        e.preventDefault();
        // saveTransactions(transaction);
    };

    useEffect(() => {
        loadBranches(setBranch);
    }, []);

    const isInitialMount = useRef(true);
    useEffect(() => {
        if (isInitialMount.current) {
            isInitialMount.current = false;
        } else {
            if (
                cities !== "undefined" &&
                selectedBranchIndex !== 0 &&
                selectedDate
            ) {
                let station_id = cities.cities.find(
                    (itm) => itm.Name === branch[selectedBranchIndex - 1].city
                ).meteorological_station.id;
                console.log("station_id", station_id);
                loadWeather(station_id, selectedDate, setWeather);
            } else setWeather(0);
        }
    });

    return (
        <>
            <h1>
                <span style={{ fontSize: "2rem", fontFamily: "Noto Color Emoji" }}>
                    {"\ud83d\udcb3"}
                </span>{" "}
                {Languages.transactions[selectedLanguage]}
            </h1>
            <form
                autoComplete="off"
                className="App"
                id="form"
                onSubmit={(e) => handleSubmitTransaction(e)}
            >
                <fieldset>
                    <legend>
                        <h2>{Languages.manual_update[selectedLanguage]}</h2>
                    </legend>
                    <h2>{Languages.branches[selectedLanguage]}</h2>
                    <h3>{Languages.name[selectedLanguage]}:</h3>{" "}
                    <select
                        defaultValue=""
                        name="branch"
                        required
                        onChange={(e) => setSelectedBranchIndex(e.target.selectedIndex)}
                    >
                        <option hidden value="">
                            {Languages.name[selectedLanguage]}
                        </option>
                        {branch.map((itm, index) => (
                            <option key={index}>{itm.name}</option>
                        ))}
                    </select>
                    &emsp;
                    <h3>{Languages.city[selectedLanguage]}:</h3>{" "}
                    <input
                        disabled="disabled"
                        readOnly
                        value={
                            selectedBranchIndex === 0
                                ? ""
                                : branch[selectedBranchIndex - 1].city
                        }
                    />
                    <br />
                    <br />
                    <h2>{Languages.date[selectedLanguage]}</h2>
                    <Calendar
                        setSelectedDate={setSelectedDate}
                        setSelectedDateData={setSelectedDateData}
                    />
                    <br />
                    <br />
                    <h2>{Languages.weather[selectedLanguage]}</h2>
                    <h3>{Languages.meteorological_station_id[selectedLanguage]}:</h3>{" "}
                    <input
                        readOnly
                        disabled="disabled"
                        value={
                            selectedBranchIndex === 0 || cities === "undefined"
                                ? ""
                                : cities.cities.find(
                                        (itm) =>
                                            itm.Name === branch[selectedBranchIndex - 1].city
                                  ).meteorological_station.id
                        }
                    />
                    &emsp;
                    <h3>
                        {Languages.meteorological_station_location[selectedLanguage]}:
                    </h3>{" "}
                    <input
                        readOnly
                        disabled="disabled"
                        value={
                            selectedBranchIndex === 0 || cities === "undefined"
                                ? ""
                                : cities.cities.find(
                                        (itm) =>
                                            itm.Name === branch[selectedBranchIndex - 1].city
                                  ).meteorological_station.name
                        }
                    />
                    &emsp;
                    <h3>{Languages.temperature[selectedLanguage]}:</h3>{" "}
                    <input readOnly disabled="disabled" value={weather} />
                    {" \u2103"}
                </fieldset>
            </form>
        </>
    );
}

A screenshot of the problem

Upvotes: 0

Views: 176

Answers (1)

Il&#234; Caian
Il&#234; Caian

Reputation: 655

You need to add the dependency array to your second useEffect(), otherwise it'll keep executing indefinitely every time the component re-renders. If you define it empty, it'll run once at the first render. If you pass some values, it'll run again when one of those values is changed. Here is an example with an empty array of dependencies a dependency array with some variables that are used inside the hook:

    useEffect(() => {
        if (
            cities !== "undefined" &&
            selectedBranchIndex !== 0 &&
            selectedDate
        ) {
            let station_id = cities.cities.find(
                (itm) => itm.Name === branch[selectedBranchIndex - 1].city
            ).meteorological_station.id;
            console.log("station_id", station_id);
            loadWeather(station_id, selectedDate, setWeather);
        } else {
          setWeather(0);
        }
    }, [cities, selectedBranchIndex, selectedDate]);

As you asked in the comments section (and it was mentioned there) you need to pass the variables you want to "listen" for changes. From the code you posted, I suppose they are cities, selectedBranchIndex, selectedDate, so you just need to add them there. Trim that based on your code/needs. You could read more about the useEffect() hook, its dependency array, and how it works at the Official React Docs

Upvotes: 1

Related Questions