Home > database >  infinite loop using useEffect
infinite loop using useEffect

Time:02-25

so creating a project that updates me on my data in firebase however i have console logged the data and its constantly coming through. How do i stop the loop essentially so it doesn't crash.

import "./App.css";

import "semantic-ui-css/semantic.min.css";
import { Container, Divider, Card } from "semantic-ui-react";
import Header from "./components/Header";
import Cards from "./components/Cards/index";
import { useState, useEffect } from "react";
import { AppProvider } from "./Context/Context.js";
import "firebase/compat/auth";
import {} from "firebase/firestore";
import * as FirestoreService from "./components/service/firebase";
import firebase from "@firebase/app-compat";

function App() {
  const [user, setUser] = useState(null);
  const [cloudFucntions, setCloudFucntions] = useState();
  const [error, setError] = useState();

  useEffect(() => {
    firebase.auth().onAuthStateChanged((user) => {
      setUser(user);

      const unsubscribe = FirestoreService.getCloudFunctionItems(
        (querySnapshot) => {
          const updatedCloundFunctions = querySnapshot.docs.map((docSnapshot) =>
            docSnapshot.data()
          );
          setCloudFucntions(updatedCloundFunctions);
          console.log(updatedCloundFunctions);
        },
        (error) => setError("list-item-get-fail")
      );
      return unsubscribe;
    });
  }, [cloudFucntions]);

  return (
    <AppProvider>
      <div>
        <Container>
          <Header />

          <Divider horizontal section>
            Cloud Function Monitor
          </Divider>

          {user ? (
            <Card.Group itemsPerRow="4">
              {cloudFucntions &&
                cloudFucntions.map((cloudFunction) => {
                  return (
                    <Cards
                      key={cloudFunction.id}
                      cloudFunction={cloudFunction}
                    ></Cards>
                  );
                })}
            </Card.Group>
          ) : (
            <h2> Please sign in using the button in the top right. </h2>
          )}
        </Container>
      </div>
    </AppProvider>
  );
}

export default App;

Image of console...

I have tried using an empty array and not passing anything through however i get the errors: Warning: Each child in a list should have a unique "key" prop. Warning: Failed prop type: Invalid prop children supplied to CardContent, expected a ReactNode. Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.

CodePudding user response:

You can't generally use a dependency for an effect that the effect ultimately updates, this is what leads to render looping. For the useEffect hook, cloudFucntions isn't referenced at all in the callback, so it's not a dependnecy. With firebase connections it's more common to use a mounting useEffect hook, i.e. an empty dependency array.

I see also that the component doesn't unsubscribe from the onAuthStateChanged check. Since the the second cloud functions fetching doesn't appear to be dependent on the user, you should split these into their own effects and subscription clean ups.

useEffect(() => {
  const unsubscribe = firebase.auth()
    .onAuthStateChanged((user) => setUser(user));
  return unsubscribe;
}, []);

useEffect(() => {
  const unsubscribe = FirestoreService.getCloudFunctionItems(
    (querySnapshot) => {
      const updatedCloundFunctions = querySnapshot
        .docs
        .map((docSnapshot) => docSnapshot.data());
      setCloudFucntions(updatedCloundFunctions);
    },
    (error) => setError("list-item-get-fail")
  );
  return unsubscribe;
}, []);

Warning: Each child in a list should have a unique "key" prop.

This only means that key={cloudFunction.id} isn't a unique enough id to be used as a React key. React keys must be unique among siblings. Don't use the array index as key unless you've no other choice. It's not recommended.

If your cloud function objects have duplicate ids, or there are duplicate cloud functions being returned you should fix this in the backend. If this isn't something you can resolve in the backend then you'll need to generate unique ids when you receive them in the frontend.

Example:

import { v4 as uuidV4 } from 'uuid';

...

const updatedCloundFunctions = querySnapshot
  .docs
  .map((docSnapshot) => ({
    guid: uuidV4(), // <-- generate your own GUID
    ...docSnapshot.data(),
  }));
setCloudFucntions(updatedCloundFunctions);

...

cloudFucntions.map((cloudFunction) => {
  return (
    <Cards
      key={cloudFunction.guid} // <-- use your GUID here as key
      cloudFunction={cloudFunction}
    />
  );
})}
Warning: Failed prop type: Invalid prop children supplied to CardContent, expected a ReactNode.
Warning: Functions are not valid as a React child.

For each of these we'd need to see the Cards component I suspect. The warnings are with regard to a CartContent component it seems. You can add the Cards component and other relevant code, along with any propTypes definitions and we can further refine answer, or you can post a new question on SO since these are rather extra-curricular to the issue of the render looping and React keys.

CodePudding user response:

You have an incorrect dependency on the useEffect. The call to setCloudFucntions will retrigger useEffect (because it sets cloudFucntions)

  useEffect(() => {
    ...
          setCloudFucntions(updatedCloundFunctions);
    ...
  }, [cloudFucntions]);

You don't need it, your effect does not use cloudFucntions.

The warning is concerning the key in the render, it seems that cloudFunction.id may not be unique.

You could do this

cloudFucntions.map((cloudFunction, idx) => {
  return (
    <Cards
      key={idx}
      cloudFunction={cloudFunction}
    ></Cards>
  )
  • Related