Home > Software design >  Why is a function inside a component called an infinite number of times?
Why is a function inside a component called an infinite number of times?

Time:04-18

I have React component :

import { Hotels } from "./Hotels";
import WelcomePage from "./WelcomePage";

import { initializeApp } from "https://www.gstatic.com/firebasejs/9.6.0/firebase-app.js";
import {
  getFirestore,
  collection,
  getDocs,
  addDoc,
} from "https://www.gstatic.com/firebasejs/9.6.0/firebase-firestore.js";
import {
  getAuth,
  signInWithEmailAndPassword,
  createUserWithEmailAndPassword,
  onAuthStateChanged,
  signOut,
} from "https://www.gstatic.com/firebasejs/9.6.0/firebase-auth.js";

import { firebaseConfig, app, db, auth } from "../firebaseConfig";

import { useState } from "react";

function MainPage() {
  const [hotels, setHotels] = useState([]);
  const [authentication, setAuthentication] = useState(false);

  async function fetchHotels() {
    const _hotels = [];
    const querySnapshot = await getDocs(collection(db, "reviews"));
    querySnapshot.forEach((doc) => {
      _hotels.push(doc.data());
    });
    console.log("fetched!");
    setHotels(_hotels);
  }
  function isAuthenticated() {
    onAuthStateChanged(auth, (user) => {
      if (user) {
        // User is signed in, see docs for a list of available properties
        const uid = user.uid;
        setAuthentication(true);
      } else {
        // User is signed out
        setAuthentication(false);
      }
    });
  }
  isAuthenticated();
  fetchHotels();

  return (
    <main className="content">
      <Hotels hotels={hotels} />
    </main>
  );
}

export default MainPage;


After the application starts, the fetchHotels function starts to be called endlessly (this is evidenced by console.log("fetched!") ).

Under the same conditions, in other components, other functions are called adequately.

CodePudding user response:

You're calling fetchHotels at the top level of your function component, so it's called on every render. In fetchHotels, you eventually call setHotels with a new array, which causes a re-render (since the new array by definition is different from the current one). So when that render happens, it calls fetchHotels again, which eventually calls setHotels again, which causes...

You need to only call fetchHotels at appropriate times. For instance, it looks like you only need to do that when the component first mounts, in which case you'd do it inside a useEffect callback with an empty dependency array (so that it is only run on component mount). And since nothing else calls it, you can just do the fetch right there in the callback:

useEffect(() => {
    let cancelled = false;
    (async () => {
        try {
            const snapshot = await getDocs(collection(db, "reviews"));
            if (!cancelled) {
                const hotels = snapshot.map(doc => doc.data());
                setHotels(hotels);
                console.log("fetched!");
            }
        } catch(error) {
            // ...handle/report error
        }
    })();
    return () => {
        // Flag so we don't try to set state when the component has been
        // unmounted. Ideally, if `getDocs` has some way of being cancelled
        // (like `AbortController`/`AbortSignal`), do that instead; using
        // a flag like this doesn't proactively stop the process.
        cancelled = true;
    };
}, []);

Note I added error handling; don't let an async function throw errors if nothing is going to handle them. Also, I used map to more idiomatically build an array (hotels) from another array (snapshot).

(You have the same basic problem with isAuthenticated. The only reason it doesn't cause an infinite loop is that it calls setAuthentication with a boolean value, so the second time it does that, it's setting the same value that was already there, which doesn't cause a re-render.)

CodePudding user response:

You can't invoke functions in a component like that. What you need to do is invoke it in useEffect and (optional) give it a parameter that triggers the useEffect function.

  • Related