Javascript 如何将来自不同组件的firebase auth和Cloud Firestore用作单个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

我试图在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.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;