skip to Main Content

First off, let me say that I probably worded my question terribly… sorry.

I currently have a useEffect in my application that when you load my page it takes the data from my Firestore collection and sets it to an array to map on screen with a component. It works perfectly fine, however after about 10 minutes of running my application I receive the error "@firebase/firestore: Firestore (9.15.0): Uncaught Error in snapshot listener: FirebaseError: [code=resource-exhausted]: Quota exceeded.".

I added a console log and it looks like this is because my useEffect is constantly trying to read the data from the collection in firestore.

My question is, is there a way to only make this useEffect update the data / run when a new collection is added or deleted / modified?

Code:

import React, { useState, useEffect, useRef } from 'react';
import '../index.css';
import './Home.css';
import Note from '../components/Note';
import { useAuth } from '../contexts/AuthContext';
import { db } from '../firebase';
import { ReactComponent as Add } from '../imgs/add.svg';
import { doc, onSnapshot, query, collection } from 'firebase/firestore';

function Home() {
    // Firebase states
    const { currentUser } = useAuth();
    const noteboardCollectionRef = collection(db, `users/${currentUser.uid}/noteboard-app`);
    // useStates
    const [notes, setNotes] = useState([]);

    //useEffect
    useEffect(()=>{
        const q = query(noteboardCollectionRef)
        const noteboardFirebase = onSnapshot(q, (querySnapshot)=>{
            let noteArr = []
            querySnapshot.forEach((doc)=>{
                noteArr.push({...doc.data(), id: doc.id})
            });
            setNotes(noteArr);
            console.log(notes)
        })
        return noteboardFirebase;
    })
    // Start of all functions
    
  return (
    <>
    <div className='home-container'>
        <div className='home-header flex'>
            <h1 className='font-carter-one'>Noteboard</h1>
            <div className='home-header-dark-container'>
                <label className='font-carter-one'>Dark Mode</label>
               <span className='home-header-dark-mode'>
                    <input type='checkbox' checked/>
                    <span className='dark-mode-slider pointer'/>
                </span> 
            </div>
        </div>
        <div className='home-body flex-center-all'>
            <div className='home-new-note flex-center-all flex-column pointer' onClick={()=>{setAddNoteModal(true)}}>
                <Add className='pointer' id='new-note'/>
                <h2 className='font-carter-one'>Add Note</h2>
            </div>
            {notes.map(((note, index) => <Note key={index} note={note} />))}
        </div>
    </div>
    </>
  )
}

export default Home;

Thank you in advanced!

3

Answers


  1. You are facing this error because you dont have dependencies in the useEffect i.e

    useEffect(()=>{...
           },[])  // You are missing this []
    

    Because of which the useEffect runs every time the page is rendered , which is causing to make unlimited requests to the server which is leading to quota exceeded error in firebase

    • If array is null i.e [] then it runs only the first time the page is rendered.

    • If you want the useEffect to run only when the firestore is changed add dependency of notes i.e [notes].

    Now useEffect will run only when there is change in notes !!

    Your final code should look like:

    useEffect(()=>{
            const q = query(noteboardCollectionRef)
            const noteboardFirebase = onSnapshot(q, (querySnapshot)=>{
                let noteArr = []
                querySnapshot.forEach((doc)=>{
                    noteArr.push({...doc.data(), id: doc.id})
                });
                setNotes(noteArr);
                console.log(notes)
            })
            return noteboardFirebase;
        }[notes])                // <-- add dependency of notes
    
    Login or Signup to reply.
  2. Add dependency to [] like this:

    useEffect(()=>{
    
      doSomething()
    
    },[dependency])
    

    when dependency is changed, doSomething() will run again.

    You can see detail in here!

    So, as for your question you should edit code to this:

        //useEffect
        useEffect(()=>{
            const q = query(noteboardCollectionRef)
            const noteboardFirebase = onSnapshot(q, (querySnapshot)=>{
                let noteArr = []
                querySnapshot.forEach((doc)=>{
                    noteArr.push({...doc.data(), id: doc.id})
                });
                setNotes(noteArr);
                console.log(notes)
            })
            return noteboardFirebase;
        },[notes])
    
    Login or Signup to reply.
  3. You could use onSnapshot() to make this happen something like this ;

    import { useEffect, useState } from 'react';
    import { firestore } from './firebase';
    
    function MyComponent() {
      const [document, setDocument] = useState(null);
    
    useEffect(() => {
    const unsubscribe = firestore
      .doc('my-collection/my-document')
      .onSnapshot((doc) => {
        setDocument(doc.data());
      });
    
    return () => {
      unsubscribe();
     };
      }, []);
    
    return (
    <div>
      {document ? (
        <div>
          <h1>{document.title}</h1>
          <p>{document.body}</p>
        </div>
      ) : (
        <p>Loading...</p>
      )}
    </div>
    );
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search