Javascript 如何将来自不同组件的firebase auth和Cloud Firestore用作单个firebase应用程序
我试图在React项目中使用firebase来提供身份验证和数据库功能 在我的Javascript 如何将来自不同组件的firebase auth和Cloud Firestore用作单个firebase应用程序,javascript,reactjs,firebase,google-cloud-firestore,Javascript,Reactjs,Firebase,Google Cloud Firestore,我试图在React项目中使用firebase来提供身份验证和数据库功能 在我的App.js中我有 import app from "firebase/app"; import "firebase/auth"; app.initializeApp(firebaseConfig); 在由App.js呈现的名为的其他组件中,我有这个来初始化数据库 import app from "firebase/app"; import "firebase/firestore"; const db = app.f
App.js中
我有
import app from "firebase/app";
import "firebase/auth";
app.initializeApp(firebaseConfig);
在由App.js
呈现的名为
的其他组件中,我有这个来初始化数据库
import app from "firebase/app";
import "firebase/firestore";
const db = app.firestore();
但是这次我犯了这个错误
Uncaught FirebaseError: Firebase: No Firebase App '[DEFAULT]' has been created - call Firebase App.initializeApp() (app/no-app).
所以我试着把app.initializeApp(firebaseConfig)代码>也在这个组件中,但我再次得到一个新错误,告诉我我实例化了两次
Uncaught FirebaseError: Firebase: Firebase App named '[DEFAULT]' already exists (app/duplicate-app).
所以我想到的一个解决方法是在App.js
和App.initializeApp(firebaseConfig)之后创建一个上下文代码>我通过const db=app.firestore()创建了数据库
并将值传递给上下文,让
使用。然而,我不知道这是否是一个好的解决方案
我的问题与你的不同,原因之一是。我并没有试图连接到第二个Firebase应用程序,因为它是为了那个问题。我的整个项目只有一个Firebase应用程序,提供两个服务:身份验证和数据库
我尝试了在
但它不起作用,它仍然给我未捕获的Firebase错误:Firebase:Firebase应用程序名为“[默认]”已经存在(应用程序/重复应用程序)。
错误您可以使用您所说的上下文或redux(使用中间件初始化,全局状态保留数据库):
FirebaseContext.js:
import React from 'react'
const FirebaseContext = React.createContext(null)
export const withFirebase = Component => props => (
<FirebaseContext.Consumer>
{firebase => <Component {...props} firebase={firebase} />}
</FirebaseContext.Consumer>
)
您可以在此处看到完整的代码:并在此处看到教程:
如果您使用redux和redux thunk实现它,您可以隔离中间件、操作和还原器中的所有firebase内容(您可以在此处获取想法和示例:);并将业务逻辑保留在组件中,这样它们就不需要知道数据收集是如何存储和管理的。组件应该只知道状态和操作。在应用程序和组件中使用不同的Firebase实例
// firebaseApp.js
import firebase from 'firebase'
const config = {
apiKey: "...",
authDomain: "...",
databaseURL: "....",
projectId: "...",
messagingSenderId: "..."
};
firebase.initializeApp(config);
export default firebase;
然后您可以从firebaseApp.js导入firebase并使用它。更多详细信息在src/firebase
目录中为firebase配置创建一个文件firebaseConfig.js
:
import firebase from 'firebase/app'; // doing import firebase from 'firebase' or import * as firebase from firebase is not good practice.
import 'firebase/auth';
import 'firebase/firestore';
// Initialize Firebase
let config = {
apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
databaseURL: process.env.REACT_APP_FIREBASE_DATABASE_URL,
projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
};
firebase.initializeApp(config);
const auth = firebase.auth();
const db = firebase.firestore();
const googleAuthProvider = new firebase.auth.GoogleAuthProvider();
const emailAuthProvider = new firebase.auth.EmailAuthProvider();
export { auth, firebase, db, googleAuthProvider, emailAuthProvider };
在Component.js
中,您只需执行以下操作:
import { db } from './firebase/firebaseConfig.js'; // Assuming Component.js is in the src folder
将api密钥存储在项目根文件夹中的.env
文件中(src
的父级):
REACT\u APP\u FIREBASE\u API\u键=
REACT\u APP\u FIREBASE\u AUTH\u域=
反应\u应用程序\u FIREBASE\u数据库\u URL=
反应\u应用\u FIREBASE\u项目\u ID=
反应\应用\火基\存储\桶=
反应\u应用程序\u FIREBASE\u消息\u发件人\u ID=
我发现在react中使用firebase的最佳方法是首先初始化并导出firebase,然后执行所需的功能
helper-firebase.js
import * as firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';
// Everyone can read client side javascript, no need to use an .env file
// I only used environment variables for firebase-admin
import { FIREBASE_CONFIG } from '../../config';
// Initialize Firebase
firebase.initializeApp(FIREBASE_CONFIG);
export const auth = firebase.auth();
export const provider = new firebase.auth.GoogleAuthProvider();
export const db = firebase.firestore();
export default firebase;
your-component.js
import {
auth,
provider,
db,
} from '../../../helpers/helper-firebase';
...
componentDidMount() {
this.usersRef = db.collection('users');
// Look for user changes
auth.onAuthStateChanged(this.authChanged);
}
authChanged(user) {
// Set value on the database
this.usersRef.doc(user.uid).set({
lastLogin: new Date(),
}, { merge: true })
.then(() => {
console.log('User Updated');
})
.catch((error) => {
console.error(error.message);
});
}
login() {
auth.signInWithPopup(provider)
.then((res) => {
console.log(newUser);
})
.catch((error) => {
console.error(error.message);
})
}
...
但我建议使用“redux thunk”来存储州数据:
redux-actions.js
import {
auth,
} from '../../../helpers/helper-firebase';
export const setUser = payload => ({
type: AUTH_CHANGED,
payload,
});
export const onAuthChange = () => (
dispatch => auth.onAuthStateChanged((user) => {
// console.log(user);
if (user) {
dispatch(setUser(user));
} else {
dispatch(setUser());
}
})
);
export const authLogout = () => (
dispatch => (
auth.signOut()
.then(() => {
dispatch(setUser());
})
.catch((error) => {
console.error(error.message);
})
)
);
您收到的错误消息是有效的,并且与模块的导入顺序有关ES6模块经过预解析,以便在执行代码之前解析进一步的导入。 假设你的
App.js
的顶部看起来像这样:
import Component from '../component';
...
import app from "firebase/app";
import "firebase/auth";
app.initializeApp(firebaseConfig);
这里的问题是,内部从“…/Component”导入组件代码>
import app from "firebase/app";
import "firebase/firestore";
const db = app.firestore();
该代码在执行以下操作之前执行:
app.initializeApp(firebaseConfig);
有很多方法可以解决这个问题,包括上面介绍的一些解决方案,以及只在firebase config.js
中存储firebase配置并导入db
从那以后
这个答案更多的是关于理解问题是什么。。。就解决方案而言,我认为您的上下文
提供者
实际上是非常好的,并且经常使用
更多关于es6模块的信息
希望能有所帮助。下面是一个简单的示例,用于将google OAuth中的登录用户数据存储到firestore集合中
将firebase配置存储在单独的文件中
firebase.utils.js
import firebase from 'firebase/app';
import 'firebase/firestore';
import 'firebase/auth';
//replace your config here
const config = {
apiKey: '*****',
authDomain: '******',
databaseURL: '******',
projectId: '******,
storageBucket: '********',
messagingSenderId: '*******',
appId: '**********'
};
firebase.initializeApp(config);
export const createUserProfileDocument = async (userAuth) => {
if (!userAuth) return;
const userRef = firestore.doc(`users/${userAuth.uid}`);
const snapShot = await userRef.get();
if (!snapShot.exists) {
const { displayName, email } = userAuth;
const createdAt = new Date();
try {
await userRef.set({
displayName,
email,
createdAt
});
} catch (error) {
console.log('error creating user', error.message);
}
}
return userRef;
};
export const auth = firebase.auth();
export const firestore = firebase.firestore();
const provider = new firebase.auth.GoogleAuthProvider();
provider.setCustomParameters({ prompt: 'select_account' });
export const signInWithGoogle = () => auth.signInWithPopup(provider);
import React from 'react';
import { auth, createUserProfileDocument, signInWithGoogle } from './firebase.utils';
class App extends React.Component {
constructor() {
super();
this.state = {
currentUser: null
};
}
unsubscribeFromAuth = null;
componentDidMount() {
this.unsubscribeFromAuth = auth.onAuthStateChanged(async userAuth => {
if (userAuth) {
const userRef = await createUserProfileDocument(userAuth);
userRef.onSnapshot(snapShot => {
this.setState({
currentUser: {
id: snapShot.id,
...snapShot.data()
}
});
console.log(this.state);
});
}
this.setState({ currentUser: userAuth });
});
}
componentWillUnmount() {
this.unsubscribeFromAuth();
}
render() {
return(
<React.Fragment>
{ this.state.currentUser ?
(<Button onClick={() => auth.signOut()}>Sign Out</Button>)
:
(<Button onClick={signInWithGoogle} > Sign in with Google </Button>)
}
</React.Fragment>
)
}
}
export default App;
App.js
import firebase from 'firebase/app';
import 'firebase/firestore';
import 'firebase/auth';
//replace your config here
const config = {
apiKey: '*****',
authDomain: '******',
databaseURL: '******',
projectId: '******,
storageBucket: '********',
messagingSenderId: '*******',
appId: '**********'
};
firebase.initializeApp(config);
export const createUserProfileDocument = async (userAuth) => {
if (!userAuth) return;
const userRef = firestore.doc(`users/${userAuth.uid}`);
const snapShot = await userRef.get();
if (!snapShot.exists) {
const { displayName, email } = userAuth;
const createdAt = new Date();
try {
await userRef.set({
displayName,
email,
createdAt
});
} catch (error) {
console.log('error creating user', error.message);
}
}
return userRef;
};
export const auth = firebase.auth();
export const firestore = firebase.firestore();
const provider = new firebase.auth.GoogleAuthProvider();
provider.setCustomParameters({ prompt: 'select_account' });
export const signInWithGoogle = () => auth.signInWithPopup(provider);
import React from 'react';
import { auth, createUserProfileDocument, signInWithGoogle } from './firebase.utils';
class App extends React.Component {
constructor() {
super();
this.state = {
currentUser: null
};
}
unsubscribeFromAuth = null;
componentDidMount() {
this.unsubscribeFromAuth = auth.onAuthStateChanged(async userAuth => {
if (userAuth) {
const userRef = await createUserProfileDocument(userAuth);
userRef.onSnapshot(snapShot => {
this.setState({
currentUser: {
id: snapShot.id,
...snapShot.data()
}
});
console.log(this.state);
});
}
this.setState({ currentUser: userAuth });
});
}
componentWillUnmount() {
this.unsubscribeFromAuth();
}
render() {
return(
<React.Fragment>
{ this.state.currentUser ?
(<Button onClick={() => auth.signOut()}>Sign Out</Button>)
:
(<Button onClick={signInWithGoogle} > Sign in with Google </Button>)
}
</React.Fragment>
)
}
}
export default App;
从“React”导入React;
从“/firebase.utils”导入{auth,createUserProfileDocument,signInWithGoogle};
类应用程序扩展了React.Component{
构造函数(){
超级();
此.state={
当前用户:空
};
}
unsubscribeFromAuth=null;
componentDidMount(){
this.unsubscribeFromAuth=auth.onAuthStateChanged(异步userAuth=>{
if(userAuth){
const userRef=await createUserProfileDocument(userAuth);
userRef.onSnapshot(快照=>{
这是我的国家({
当前用户:{
id:snapShot.id,
…snapShot.data()
}
});
console.log(this.state);
});
}
this.setState({currentUser:userAuth});
});
}
组件将卸载(){
这是。unsubscribeFromAuth();
}
render(){
返回(
{this.state.currentUser?
(auth.signOut()}>注销)
:
(使用谷歌登录)
}
)
}
}
导出默认应用程序;
我建议您在希望在组件之间共享状态时使用存储管理库。在本例中,我们在单个组件中完成了所有操作。但在实时情况下,在这种用例中,您可能有一个复杂的组件体系结构,使用存储管理库可能会派上用场。您好,感谢您的及时回复。我看到您将firebase config
文件放在.env
中。这被认为是一种更好的做法,而不仅仅是将其存储在js文件firebase config.js
中并从中导入?还有为什么使用Redux来管理数据库实例(即db
)比只从“/db”导入{db}
更好,只要组件需要db
。最好将您的凭证放在您不打算上传到存储库的文件中(git、mercurial等)。将其保存在环境文件中可以让您拥有不同的配置,例如
app.initializeApp(firebaseConfig);
import firebase from 'firebase/app';
import 'firebase/firestore';
import 'firebase/auth';
//replace your config here
const config = {
apiKey: '*****',
authDomain: '******',
databaseURL: '******',
projectId: '******,
storageBucket: '********',
messagingSenderId: '*******',
appId: '**********'
};
firebase.initializeApp(config);
export const createUserProfileDocument = async (userAuth) => {
if (!userAuth) return;
const userRef = firestore.doc(`users/${userAuth.uid}`);
const snapShot = await userRef.get();
if (!snapShot.exists) {
const { displayName, email } = userAuth;
const createdAt = new Date();
try {
await userRef.set({
displayName,
email,
createdAt
});
} catch (error) {
console.log('error creating user', error.message);
}
}
return userRef;
};
export const auth = firebase.auth();
export const firestore = firebase.firestore();
const provider = new firebase.auth.GoogleAuthProvider();
provider.setCustomParameters({ prompt: 'select_account' });
export const signInWithGoogle = () => auth.signInWithPopup(provider);
import React from 'react';
import { auth, createUserProfileDocument, signInWithGoogle } from './firebase.utils';
class App extends React.Component {
constructor() {
super();
this.state = {
currentUser: null
};
}
unsubscribeFromAuth = null;
componentDidMount() {
this.unsubscribeFromAuth = auth.onAuthStateChanged(async userAuth => {
if (userAuth) {
const userRef = await createUserProfileDocument(userAuth);
userRef.onSnapshot(snapShot => {
this.setState({
currentUser: {
id: snapShot.id,
...snapShot.data()
}
});
console.log(this.state);
});
}
this.setState({ currentUser: userAuth });
});
}
componentWillUnmount() {
this.unsubscribeFromAuth();
}
render() {
return(
<React.Fragment>
{ this.state.currentUser ?
(<Button onClick={() => auth.signOut()}>Sign Out</Button>)
:
(<Button onClick={signInWithGoogle} > Sign in with Google </Button>)
}
</React.Fragment>
)
}
}
export default App;