I am creating a todolist app paired with Google Firebase to store my data. In the app, you can create projects to organise your tasks. I am using useContext to have my projects accessible accross all the components of my app. My context uses a custom hook to make a call to firebase when mounting to get the projects of the logged in user (or an empty array if the user hasn't got any projects stored in firebase).
My issue is that my contextProvider goes into a mounting loop, making a new call to firebase to set the projects every time it mounts (even though the projects are not changing). The context is actually working in my app, I only realised it was looping because I have reached the maximum amount of calls that can be made to firebase on the freeplan in a day.
Here's my code for for the context:
import React, { createContext, useContext } from "react";
import PropTypes from "prop-types";
import { useProjects } from "../Hooks/index";
export const ProjectsContext = createContext();
export const ProjectsProvider = ({ children }) => {
const { projects, setProjects } = useProjects();
return (
<ProjectsContext.Provider value={{ projects, setProjects }}>
{children}
</ProjectsContext.Provider>
);
};
export const useProjectsValue = () => useContext(ProjectsContext);
and here is my code for the custom hook useProjects() that makes a call to firebase to retrieve the projects:
export const useProjects = () => {
const [projects, setProjects] = useState([]);
useEffect(() => {
db.collection("projects")
.where("userId", "==", "uBnTwu5ZfuPa5BE0xlkL")
.orderBy("projectId")
.get()
.then((snapshot) => {
const allProjects = snapshot.docs.map((project) => ({
...project.data(),
docId: project.id,
}));
if (JSON.stringify(allProjects) !== JSON.stringify(projects)) {
setProjects(allProjects);
}
});
}, [projects]);
return { projects, setProjects };
};
Also, I am console logging in the hook and in the context to check how many times it is beeing called, is it good practice or is there a better way to check how many times a component mounts ?
Thanks in advance
Your code gets stuck into a loop because the useEffect
hook contains projects
as a dependency. As the code in the useEffect
updates projects
, your code gets stuck in a endless cycle of state update and re-render.
Looking at your code, it seems that useEffect
uses projects
in the if
statement
if (JSON.stringify(allProjects) !== JSON.stringify(projects)) {
setProjects(allProjects);
}
and that seems like something you shouldn't be doing in the first place.
You could observe the projects
collection if you always want to receive real-time updates, otherwise the useEffect
hook should only run once.
useEffect
uses setProjects()
function but it is guaranteed to not change, so adding or omitting it from the dependency array of the useEffect
hook won't make a difference.