So, I created this small app to practice with Firebase and on the first day I get "Quota exceeded" error. I’m pretty sure that I didn’t send that much requests on my own, there must be something that causes so much requests, but I don’t know what. Here is the code:
Auth.js:
import { useEffect, useState } from 'react';
import { auth, googleProvider } from '../config/firebase';
import { createUserWithEmailAndPassword, signInWithPopup, signOut } from 'firebase/auth';
export const Auth = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
useEffect(() => {
console.log(auth?.currentUser?.email);
}, [auth?.currentUser?.email]);
const signIn = async () => {
try {
await createUserWithEmailAndPassword(auth, email, password);
} catch (err) {
console.error(err);
}
}
const signInWithGoogle = async () => {
try {
await signInWithPopup(auth, googleProvider);
} catch (err) {
console.error(err);
}
}
const logout = async () => {
try {
await signOut(auth);
} catch (err) {
console.error(err);
}
}
return (
<div>
<input
type="email"
placeholder="Email..."
onChange={(e) => setEmail(e.target.value)} />
<input
type="password"
placeholder="Password..."
onChange={(e) => setPassword(e.target.value)} />
<button onClick={signIn}>Sign In</button>
<button onClick={signInWithGoogle}>Sign In With Google</button>
<button onClick={logout}>Log out</button>
</div>
)
}
App.js
import { useEffect, useMemo, useState } from 'react';
import { Auth } from './components/auth';
import { db, auth, storage } from './config/firebase';
import { ref, uploadBytes } from 'firebase/storage';
import {
getDocs,
collection,
addDoc,
deleteDoc,
updateDoc,
doc } from 'firebase/firestore';
import './App.css';
function App() {
// movies from db collection
const [movieList, setMovieList] = useState([]);
const moviesCollectionRef = useMemo(() => collection(db, "movies"), []);
// New movie states
const [newMovieTitle, setNewMovieTitle] = useState('');
const [newReleaseDate, setNewReleaseDate] = useState(0);
const [isNewMovieOscar, setIsNewMovieOscar] = useState(false);
// Update title state
const [updatedTitle, setUpdatedTitle] = useState('');
// File upload state
const [fileUpload, setFileUpload] = useState(null);
const onSubmitMovie = async () => {
try {
await addDoc(moviesCollectionRef, {
title: newMovieTitle,
releaseDate: newReleaseDate,
recievedAnOscar: isNewMovieOscar,
userId: auth?.currentUser?.uid
});
} catch (err) {
console.error(err);
}
}
const deleteMovie = async (id) => {
const movieDoc = doc(db, "movies", id);
await deleteDoc(movieDoc);
}
const updateMovieTitle = async (id) => {
const movieDoc = doc(db, "movies", id);
await updateDoc(movieDoc, { title: updatedTitle });
}
useEffect(() => {
const getMovieList = async () => {
try {
const data = await getDocs(moviesCollectionRef);
const filteredData = data.docs.map(doc => ({
...doc.data(),
id: doc.id
}))
setMovieList(filteredData);
} catch (err) {
console.error(err);
}
}
getMovieList();
}, [onSubmitMovie])
const uploadFile = async () => {
if (!fileUpload) return;
const filesFolderRef = ref(storage, `projectFiles/${fileUpload.name}`);
try {
await uploadBytes(filesFolderRef, fileUpload);
} catch (err) {
console.error(err);
}
}
return (
<div className="App">
<Auth />
<div>
<input
type="text"
placeholder='Movie title...'
onChange={(e) => setNewMovieTitle(e.target.value)} />
<input
type="number"
placeholder='Release date...'
onChange={(e) => setNewReleaseDate(Number(e.target.value))} />
<input
type="checkbox"
checked={isNewMovieOscar}
onChange={(e) => setIsNewMovieOscar(e.target.checked)} />
<label>Recieved an Oscar</label>
<button onClick={onSubmitMovie}>Submit movie</button>
</div>
<div>
{movieList.map(movie => (
<div key={movie.id}>
<h1 style={{color: movie.recievedAnOscar ? 'green' : 'red'}}>{movie.title}</h1>
<p>Date: {movie.releaseDate}</p>
<button onClick={() => deleteMovie(movie.id)}>Delete movie</button>
<input
type="text"
placeholder='New title...'
onChange={(e) => setUpdatedTitle(e.target.value)} />
<button onClick={() => updateMovieTitle(movie.id)}>Update Title</button>
</div>
))}
</div>
<div>
<input
type="file"
onChange={(e) => setFileUpload(e.target.files[0])} />
<button onClick={uploadFile}>Upload File</button>
</div>
</div>
);
}
export default App;
I don’t know, maybe I should wrap all my functions into useCallback() or what, there must be something to it. If you already encountered this problem, please help
2
Answers
Because you put
onSubmitMovie
in the dependency array, this effect will run every time the component renders. So you can very quickly rack up many many calls to get the data.There does not seem to be any reason for including that in the dependency array, since it’s never used. Change it to an empty array to only get the movies on mount
An empty dependency array is not best practice for each movie update (also you can manage this with global state for less data query) and is not allowed by default with the Next.js ESLint control, which will throw a warning for this. Instead, you should use appropriate trigger dependencies, such as when the form is ready to send, all fields are filled, etc. to fetch list from the database.
Example code:
Be sure your security settings are correct on firestore;
https://firebase.google.com/docs/firestore/security/get-started
Also, consider adding extra security layers for database update methods, such as server-side database connections and authentication APIs to the client, etc.
https://nextjs.org/blog/security-nextjs-server-components-actions