I'm trying to integrate Firebase into my React app and after looking at various tutorials, I can't seem to find a consensus on where to put the firebase initialization code firebase.initializeApp(config).
My app's structure is this:
- app.js
- components
|___Index.jsx
|___Layout.jsx
|___PageContent.jsx
Each file looks like the following
app.js
Does all the express setup with server-side rendering
Index.jsx
import React from 'react';
import Layout from './Layout.jsx';
import PageContent from './PageContent.jsx';
import './global.css';
class Index extends React.Component {
render() {
return (
<Layout title={this.props.title}>
<PageContent />
</Layout>
);
}
}
export default Index;
PageContent.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import LandingPage from './components/Landing_Page/Landing.jsx';
class PageContent extends React.Component {
render() {
return (
<LandingPage />
);
}
}
if (typeof window !== 'undefined') {
ReactDOM.render(
<PageContent />,
document.getElementById('root')
);
}
export default PageContent;
I need to make sure Firebase is available in every page of my website. Right now it is a single page app but eventually I'll be adding more.
Can anybody help me understand where I might put the database initialization code so that it is applied everywhere?
this is how I do it
create file Firebase.js next to app.js and put your initialization code
Firebase.js file
import * as firebase from 'firebase';
let config = {
apiKey: "XXXXXXX",
authDomain: "XXXXX",
databaseURL: "XXXXX",
projectId: "XXXXX",
storageBucket: "XXXX",
messagingSenderId: "XXXX"
};
firebase.initializeApp(config);
export default firebase;
and then import Firebase.js file every where you want to use it , for example
import React from 'react';
import firebase from './../Firebase.js' // <------ import firebase
class Test extends React.Component {
componentDidMount(){
firebase.database().ref().child("X").on('value' , {...});
}
render() {
return (
<h1>Hello</h1>
);
}
}
export default Test;
Create file with name firebase.js
import firebase from "firebase/app";
import "firebase/auth";
import "firebase/firestore";
const firebaseConfig = {
apiKey: 'AIzaXXXXXXXXXXXXXXXXXXXXXXX',
authDomain: 'test-XXXX.firebaseapp.com',
databaseURL: 'https://test-db.firebaseio.com',
projectId: 'test-XXXX',
storageBucket: 'test-bucket.appspot.com',
messagingSenderId: 'XXXXXXX',
appId: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
};
firebase.initializeApp(firebaseConfig);
export const firebaseAuth = firebase.auth();
export const firestore = firebase.firestore()
Then use it as follows:
import { firebaseAuth } from 'firebase'
import SigninForm from 'signin.js'
const ModalSignIn = props => {
const [email, setEmail] = useState()
const [password, setPassword] = useState()
const handleSubmit = e => {
e.preventDefault()
firebaseAuth
.signInWithEmailAndPassword(email, password)
.then(res => {
console.log(res.user)
})
.catch(err => {
const { code, message } = err
console.log(code, message)
})
}
return (<SigninForm/>)
It seems that what you want is that firebase can be globally available to your react components.
It's the same question before using redux, how does react components consume the global state tree? Global variables? Come on, we'll know global variables are evil. React props? It seems very react but passing props down from root to the leaf components can't be fun.
Context API comes to rescue. You simply need firebase bindings for react just as redux/react-router did.
Assume you don't want to reinvent wheels,
If you use redux, consider react redux firebase
If you hates boilerplates, consider react firebase
and of course you can also build your own react firebase bindings through react context as just mentioned.
Related
I'm working on a project where I need to implement Firestore database in a React app, which I do like this: I have a firebase.js file in the src directory which contains information about Firebase and exports a db object,
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";
// Firebase project configuration
const firebaseConfig = {
apiKey: ...
authDomain: ...
databaseURL: ...
projectId: ...
storageBucket: ...
messagingSenderId: ...
appId: ...
measurementId: ...
};
const app = initializeApp(firebaseConfig);
// Get a reference to the database service
const db = getFirestore(app);
export { db }
Then I try to import the db object from App.js,
import React, { Component } from 'react';
import { db } from './firebase'
class App extends Component {
render() {
return (
<div>
<h1>Hello</h1>
</div>
);
}
}
export default App;
And I get the following error in the console Uncaught SyntaxError: Cannot use import statement outside a module (at bundle.js:45109:2), as a result, the component does not render.
The line in which it seems to be a problem in bundle.js:45109:2 is this:
import { registerVersion } from '#firebase/app';
I looked at the suggestions online for how to fix firebase, but they didn't work. I tried setting my firebase.json hosting feature where says "public" to "build", but that didn't work so what else should I do? I don't get errors when I compile it, but the website is blank when I run "npm start". Here is the relevant javascript code:
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';
import 'firebase/compat/firestore';
const firebaseApp = firebase.initializeApp({
apiKey: "AIzaSyBl_kNHH8oIn17VrR2mcKQqOn3eAZo-Osw",
authDomain: "instagram-clone-react-82ab7.firebaseapp.com",
projectId: "instagram-clone-react-82ab7",
storageBucket: "instagram-clone-react-82ab7.appspot.com",
messagingSenderId: "562669348604",
appId: "1:562669348604:web:ae6a7cee3832803e761979",
measurementId: "G-6PENZ2M8LS"
});
const db = firebaseApp.firestore();
const auth = firebase.auth();
const storage = firebase.storage();
export { db, auth, storage };
export default db;
App.js file code:
import React, { useState, useEffect } from 'react';
import './App.css';
import Post from './Post';
import { db } from './firebase';
function App() {
const [posts, setPosts] = useState([]);
//useEffect: Runs a piece of code based on a specific condition
useEffect(() => {
//this is where the code runs
db.collection('posts').onSnapshot(snapshot => {
//Everytime a new post is added, this line of code activates
setPosts(snapshot.docs.map(doc => doc.data()))
}) //"posts" inside of firebase also everytime a document gets modified inside of post it takes a screenshot
}, [] ); //conditions go here and there just variables
return (
<div className="App">
<div className="app__header">
<img
className="app__headerImage"
src="https://www.instagram.com/static/images/web/mobile_nav_type_logo.png/735145cfe0a4.png"
alt="instagram_text"
/>
</div>
<h1>Hello clever programmers let's build a react app!!!</h1>
{
posts.map(post => (
<Post username={post.username} caption={post.caption} imageUrl={post.imageUrl} />
))
}
</div>
);
}
export default App;
error in browser:
The issue is that you haven't imported Firebase Storage.
To fix it, simply add the import statement after you import firebase/compat/app, like below.
import firebase from "firebase/compat/app";
// After you import app...
import "firebase/compat/storage";
Now, when you call the function below with .ref(), it should work correctly.
const storage = firebase.storage().ref();
Try to use
const storage = firebase.storage().ref();
instead of
const storage = firebase.storage();
I have installed Firebase with npm in Firebase, but I don't know what happens with this error.
File FIREBASE.js
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: "AIzaSyAd2EBoYYCRWEc3oClZTV3Wo-TiQkM2MgQ",
authDomain: "crud-react-26836.firebaseapp.com",
databaseURL: "https://crud-react-26836-default-rtdb.asia-southeast1.firebasedatabase.app",
projectId: "crud-react-26836",
storageBucket: "crud-react-26836.appspot.com",
messagingSenderId: "741718079918",
appId: "1:741718079918:web:1566301b46c4448c8c703f"
};
// Initialize Firebase
const FIREBASE = initializeApp(firebaseConfig);
export default FIREBASE;
TambahKontak.js
import React, { Component } from 'react'
import { StyleSheet, View, TouchableOpacity, Text, Alert } from 'react-native'
import { InputData } from '../../component'
import { FIREBASE } from '../../config/FIREBASE'
onSubmit = () => {
if (this.state.nama && this.state.nomorHP && this.state.alamat) {
console.log("Masuk Submit");
console.log(this.state);
const kontakReferensi = FIREBASE.database().ref('kontak');
You aren't using the new Modular/Functional syntax which is included from version 9.0.0+. You would have to rewrite your code to follow the new syntax:
import { getDatabase } from "firebase/database"
const dbRef = ref(getDatabase());
const snapshot = await get(child(dbRef, 'kontak'))
If you want to use the existing code (with older syntax) then use compat version by changing the imports to:
import firebase from 'firebase/compat/app'
import 'firebase/compat/database'
import 'firebase/compat/[SERVICE_NAME]'
const FIREBASE = firebase.initializeApp(firebaseConfig);
export default FIREBASE;
I'd recommend using the new version and following the documentation for learning more about it.
This question already has answers here:
Warning: It looks like you're using the development build of the Firebase JS SDK
(6 answers)
Closed 2 years ago.
I am trying to use firebase database for my project but I am unable to properly import and use the module. I have followed what the official documentation recommends.
This is the code where I am using it.
import React from "react";
import firebase from "firebase";
import ProjectItemCards from "./components/project-item-cards";
const ProjectContext = React.createContext();
class ProjectProvider extends React.Component {
constructor() {
super();
this.state = {
social: {},
projects: [],
featuredProjects: [],
loading: true,
};
}
componentDidMount() {
var ref = firebase.database().ref("projects");
ref.on("value", (snapshot) => {
const projects = snapshot.val();
console.log(projects);
const featuredProjects = projects
.map((project) => (project.featured === true ? project : null))
.slice(0, 4);
this.setState({
projects,
featuredProjects,
loading: false,
});
});
}
getProjectElements(projects) {
const projectElementList = projects.map((project, index) => {
return (
<ProjectItemCards key={index} project={project}></ProjectItemCards>
);
});
return projectElementList;
}
render() {
return (
<ProjectContext.Provider
value={{ ...this.state, getProjectElements: this.getProjectElements }}
>
{this.props.children}
</ProjectContext.Provider>
);
}
}
export { ProjectProvider, ProjectContext };
I have initialized firebase as follows
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import "bootstrap";
import "./sass/main.scss";
import * as firebase from "firebase/app";
import "firebase/analytics";
var firebaseConfig = {
apiKey: process.env.REACT_APP_API_KEY,
authDomain: "",
databaseURL: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: "1:841600272388:web:12314d1260dded0601cd51",
measurementId: "G-55E4QT6C4F",
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
firebase.analytics();
ReactDOM.render(<App />, document.getElementById("root"));
I have removed all sensitive information from above code
Also note that I am getting my data as required and everything is working fine. I just want to get rid of this warning
initialization code should be used in a utility file and look like the following:
import * as firebase from 'firebase/app'
import 'firebase/firestore'
import 'firebase/storage'
const firebaseConfig = {
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,
appId: process.env.REACT_APP_FIREBASE_APP_ID
}
firebase.initializeApp(firebaseConfig)
export const db = firebase.firestore()
export const storage = firebase.storage()
export default firebase
you want to export the individual firebase features you are utilizing. In your case I would assume firebase.firestore
then your import should look like so:
import {db} from '../utils/firebase'
I am trying to use firebase in my React project to provide the auth and database functionalities.
In my App.js I have
import app from "firebase/app";
import "firebase/auth";
app.initializeApp(firebaseConfig);
In my other components called <Component /> rendered by App.js I have this to initialize the database
import app from "firebase/app";
import "firebase/firestore";
const db = app.firestore();
However this time I got this error
Uncaught FirebaseError: Firebase: No Firebase App '[DEFAULT]' has been created - call Firebase App.initializeApp() (app/no-app).
So I tried to put app.initializeApp(firebaseConfig); in this component too but I got a new error again to tell me I instantiated twice.
Uncaught FirebaseError: Firebase: Firebase App named '[DEFAULT]' already exists (app/duplicate-app).
So one workaround I came up with is to create a context at App.js and right after app.initializeApp(firebaseConfig); I created the database by const db = app.firestore(); and pass the value to the context and let the <Component /> to consume. However I don't know if this is a good solution or not.
My question is different from How to check if a Firebase App is already initialized on Android for one reason. I am not trying to connect to a second Firebase App as it was for that question. There is only one Firebase App for my entire project, to provide two services: auth and database.
I tried the solution from that question to use in <Component />
if (!app.apps.length) {
app.initializeApp(firebaseConfig);
}
const db = app.firestore();
but it didn't work it still gives me Uncaught FirebaseError: Firebase: Firebase App named '[DEFAULT]' already exists (app/duplicate-app). error
You use different instances of Firebase in App and Component.
// firebaseApp.js
import firebase from 'firebase'
const config = {
apiKey: "...",
authDomain: "...",
databaseURL: "....",
projectId: "...",
messagingSenderId: "..."
};
firebase.initializeApp(config);
export default firebase;
Than you can import firebase from firebaseApp.js and use it. More details here
Make a file firebaseConfig.js in src/firebase directory for firebase configuration:
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 };
All you have to do in Component.js is:
import { db } from './firebase/firebaseConfig.js'; // Assuming Component.js is in the src folder
Store the api keys in a .env file in the root folder of the project (the parent of src):
REACT_APP_FIREBASE_API_KEY=<api-key>
REACT_APP_FIREBASE_AUTH_DOMAIN=<auth-domain>
REACT_APP_FIREBASE_DATABASE_URL=<db-url>
REACT_APP_FIREBASE_PROJECT_ID=<proj-name>
REACT_APP_FIREBASE_STORAGE_BUCKET=<storage-bucket>
REACT_APP_FIREBASE_MESSAGING_SENDER_ID=<message-sender-id>
The error message you are receiving is valid and has to do with the order your modules are imported. ES6 modules are pre-parsed in order to resolve further imports before code is executed.
Assuming the very top of your App.js looks something like this:
import Component from '../component';
...
import app from "firebase/app";
import "firebase/auth";
app.initializeApp(firebaseConfig);
The problem here is that inside import Component from '.../component';
import app from "firebase/app";
import "firebase/firestore";
const db = app.firestore();
That code gets executed before you do:
app.initializeApp(firebaseConfig);
There's many ways to fix this problem including some solutions presented above and the proposal to just store your firebase config in a firebase-config.js and import db
from that.
This answer is more about understanding what the problem was ... and as far as the solution I think your Context Provider is actually really good and commonly practiced.
More about es6 modules here
Firebase React Setup
Hope that helps.
You can use a context as you said or redux (using a middleware to initialize, and global state to keep the db):
// Main (for example index.js)
<FirebaseContext.Provider value={new Firebase()}>
<App />
</FirebaseContext.Provider>
Firebase.js:
import app from 'firebase/app'
import 'firebase/firestore'
const config = {
apiKey: process.env.API_KEY,
databaseURL: process.env.DATABASE_URL,
projectId: process.env.PROJECT_ID,
storageBucket: process.env.STORAGE_BUCKET
}
export default class Firebase {
constructor() {
app.initializeApp(config)
// Firebase APIs
this._db = app.firestore()
}
// DB data API
data = () => this._db.collection('yourdata')
...
}
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>
)
Then you can use withFirebase in your container components:
class YourContainerComponent extends React.PureComponent {
state = {
data: null,
loading: false
}
componentDidMount() {
this._onListenForMessages()
}
_onListenForMessages = () => {
this.setState({ loading: true }, () => {
this.unsubscribe = this.props.firebase
.data()
.limit(10)
.onSnapshot(snapshot => {
if (snapshot.size) {
let data = []
snapshot.forEach(doc =>
data.push({ ...doc.data(), uid: doc.id })
)
this.setState({
data,
loading: false
})
} else {
this.setState({ data: null, loading: false })
}
})
})
})
}
componentWillUnmount() {
if (this._unsubscribe) {
this._unsubscribe()
}
}
}
export default withFirebase(YourContainerComponent)
You can see the whole code here: https://github.com/the-road-to-react-with-firebase/react-firestore-authentication and a tutorial here: https://www.robinwieruch.de/complete-firebase-authentication-react-tutorial/
If you implement it using redux, and redux-thunk you can isolate all firebase stuff in middleware, actions, and reducers (you can take ideas and sample here: https://github.com/Canner/redux-firebase-middleware); and keep the business logic in your components so they do not need to know how your data collections are stored and managed. The components should know only about states and actions.
The best way I have found to use firebase in react is to first initialize and export firebase to then execute the desired functions.
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);
})
}
...
But i would recommend use 'redux-thunk' to store data on state:
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);
})
)
);
Here is a simple example of storing the signed-in user data from google OAuth into firestore collection.
Store firebase config in a separate file
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);
App.js
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;
I suggest you use store management libraries like Redux when you want to share the state between components. In this example, we have finished everything in a single component. But in realtime, you may have a complex component architecture in such use case using store management libraries may come in handy.