我创建了这个小应用程序来练习 Firebase,第一天,我收到“超出配额”错误。我很确定我自己没有发送那么多请求,一定有什么东西导致了这么多请求,但我不知道是什么。
这是代码:
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>
)
}
应用程序.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;
我应该将所有函数包装成
useCallback()
还是什么?
useEffect(() => {
const getMovieList = async () => {
// ...
}
getMovieList();
}, [onSubmitMovie])
因为您将
onSubmitMovie
放入依赖数组中,所以每次组件渲染时都会运行此效果。因此,您可以非常快速地拨打许多电话来获取数据。
似乎没有任何理由将其包含在依赖项数组中,因为它从未被使用过。将其更改为空数组以仅获取挂载上的电影
useEffect(() => {
const getMovieList = async () => {
// ...
}
getMovieList();
}, []);
空依赖数组并不是每次电影更新的最佳实践(您也可以使用全局状态来管理它以减少数据查询),并且默认情况下 Next.js ESLint 控件不允许使用空依赖数组,这将引发警告。相反,您应该使用适当的触发器依赖项,例如当表单准备好发送、所有字段都已填写等时,从数据库中获取列表。
示例代码:
...
const onSubmitMovie = async () => {
if (!isFormFilled) return; //check all required inputs filled
try {
await addDoc(moviesCollectionRef, {
title: newMovieTitle,
releaseDate: newReleaseDate,
recievedAnOscar: isNewMovieOscar,
userId: auth?.currentUser?.uid
});
setMoviesUpdated(prev => !prev);
} catch (err) {
console.error(err);
}
}
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();
}, [moviesUpdated]);
...
确保您的 firestore 安全设置正确;
https://firebase.google.com/docs/firestore/security/get-started
此外,考虑为数据库更新方法添加额外的安全层,例如服务器端数据库连接和客户端的身份验证 API 等。
https://nextjs.org/blog/security-nextjs-server-components-actions