0

why this simple code leads to infinite loop rendering Projects component and its child(naturally) and because of useEffect infinite loop accessing firebase? managing state with contextProvider which has imported useProjects custom hook.

custom hook :

export const useProjects = () => {
  const [projects, setProjects] = useState([]);
  useEffect(() => {
    const db = firebase
      .firestore()
      .collection("projects")
      .where("userId", "==", "1234567890")
      .orderBy("projectId")
      .get();
    db.then((result) => {
      const allProjects = result.docs.map((project) => ({
        docId: project.id,
        ...project.data(),
      }));

      if (JSON.stringify(allProjects) !== JSON.stringify(projects)) {
        setProjects(allProjects);
      }
    }).catch((err) => {
      console.log("useProjects error", err);
    });
  }, [projects]);
  return { projects, setProjects };
};

Projects component:

const Projects = () => {
  const [active, setActive] = useState(null);
  const { setSelectedProject } = useSelectedProjectValue();
  const { projects } = useProjectsValue();

  return (
    projects &&
    projects.map((project) => (
      <li
        key={Math.random()}
        className={
          active === project.projectId
            ? "sidebar__project active"
            : "sidebar__project"
        }
      >
        <div
          onClick={() => {
            setActive(project.projectId);
            setSelectedProject(project.projectId);
          }}
        >
          <IndividualProject
            className="individualProjectComp"
            project={project}
          />
        </div>
      </li>
    ))
  );
};
Nicholas Tower
  • 72,740
  • 7
  • 86
  • 98
Mahmoud
  • 11
  • 4

1 Answers1

1

You are setting projects state within useEffect which takes projects as a dependency itself and even though you are comparing project value before setting, some change while stringifying the value is causing the state update each time.

You can avoid it by not adding projects as a dependency to useEffect since projects is only being updated inside useEffect and not anywhere else

export const useProjects = () => {
  const [projects, setProjects] = useState([]);
  useEffect(() => {
    const db = firebase
      .firestore()
      .collection("projects")
      .where("userId", "==", "1234567890")
      .orderBy("projectId")
      .get();
    db.then((result) => {
      const allProjects = result.docs.map((project) => ({
        docId: project.id,
        ...project.data(),
      }));

      setProjects(allProjects);
    }).catch((err) => {
      console.log("useProjects error", err);
    });
  }, []);
  return { projects, setProjects };
};
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • tanx a lot , what about the warning it gives me that we better not to use empty square brackets as second argument? – Mahmoud Jul 21 '21 at 19:02
  • The ESLint warning is there to prompt you incase you are missing a dependency that you otherwise should have added. however if you are sure what you are doing is right, you can silence the warning. Check this post for more details: https://stackoverflow.com/questions/55840294/how-to-fix-missing-dependency-warning-when-using-useeffect-react-hook/55854902#55854902 – Shubham Khatri Jul 21 '21 at 19:07