Search code examples
reactjsfirebasegoogle-cloud-firestoreuse-effect

Firebase firestore onSnapshot not working properly in useEffect


I am having some problems with firebase, I am using firestore onSnapshot to get realtime updates inside an useEffect like this:

  useEffect(() => {
  const unsubscribe = () => {
    firebase
      .firestore()
      .collection("posts")
      .orderBy("createdAt", "desc")
      .onSnapshot((post) => {
        const data = post.docs.map((doc) => ({
          id: doc.id,
          ...doc.data(),
        }));
        setPosts(data);
        setLoading(false);
      });
  };
  return () => unsubscribe();
}, []);

But it does not work, I do not get the data when the components mounts, the weird fact is that when I use it without returning the unsubscribe function it work perfectly. like this:

useEffect(() => {
firebase
  .firestore()
  .collection("posts")
  .orderBy("createdAt", "desc")
  .onSnapshot((post) => {
    const data = post.docs.map((doc) => ({
      id: doc.id,
      ...doc.data(),
    }));

    setPosts(data);
    setLoading(false);
  });

}, []);

I really would like to know why the first approach is not working, which is the ideal way to do it. I am also using React Router DOM, here you can see my Posts component which renders a Post component when I get the data.

     export default function Posts() {
      const [posts, setPosts] = useState([]);
      const [loading, setLoading] = useState(true);
    
    
      useEffect(() => {
        console.log("data listener...");
        const unsubscribe = () => {
          firebase
            .firestore()
            .collection("posts")
            .orderBy("createdAt", "desc")
            .onSnapshot((post) => {
              console.log("el console desde posts listeners..");
              const data = post.docs.map((doc) => ({
                id: doc.id,
                ...doc.data(),
              }));
              setPosts(data);
              setLoading(false);
            });
        };
        return () => unsubscribe();
      }, []);
//Not working
    
      return (
        <main className="posts">
          {loading && <h3>Loading posts...</h3>}
          {posts && posts.map((post) => <Post {...post} key={post.id} />)}
        </main>
      );
    }

This Posts component is being rendered by:

  export default function Home() {
  return (
    <div className="home">
      <Nav />
      <Posts />
    </div>
  );
}

Solution

  • That's because your listener is wrapped in side of another function and hence that won't be called unless you invoke the unsubscribe function.

    const unsubscribe = () => { firebase.collection("posts").... }
    

    Try this instead

    const unsubscribe = firestore.collection("posts")....
    
    // now calling, unsubscribe() will detach the listenter