VueJS Firebase app problem of export default error - javascript

I'm trying to create crud app with vue 2 and firebase latest, &
this is my firebase.js file
import firebase from 'firebase/app'
import 'firebase/auth'
import 'firebase/firestore'
const firebaseConfig = {
stuff
};
firebase.initializeApp(firebaseConfig);
const database = firebase.firestore()
const auth = firebase.auth()
const usersCollection = database.collection('users')
export{
database,
auth,
usersCollection
}
and here is my store/index.js file
import Vue from "vue";
import Vuex from "vuex";
import fb from "../../firebase"
import router from "../router";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
userProfile:{}
},
mutations: {
setUserProfile(state,val)
{
state.userProfile=val
},
setPerformingRequest(state,val)
{
state.performingRequest=val
}
},
actions: {
async login({dispatch},form)
{
const{user} = await fb.auth().signInWithEmailAndPassword(form.email,form.password)
dispatch('fetchUserProfile',user)
},
async signUp({dispatch},form)
{
const {user} = await fb.auth().createUserWithEmailAndPassword(form.email,form.password)
// create user object in userCollection
await fb.usersCollection.doc(user.uid).set({
firstName:form.firstName,
middleName:form.middleName,
lastName:form.lastName,
email:form.email,
password:form.password,
gender:form.gender,
age:form.user_age
})
dispatch('fetchUserProfile',user)
},
async fetchUserProfile({commit},user)
{
// fetching user profile data into constant named userProfile
const userProfile = await fb.usersCollection.doc(user.uid).get()
// setting the fetched data from firebase to state of userProfile
commit('setUserProfile',userProfile.data())
// now changing route to dashboard
if(router.currentRoute.path ==='/')
{
router.push('/Dashboard')
}
},
async logOut({commit})
{
// log user out
await fb.auth.signOut()
// clear user data from state
commit('setUserProfile',{})
// changing route to homepage
router.push('/')
}
},
modules: {},
});
the application runs with warning in browser console Uncaught (in promise) TypeError: firebase__WEBPACK_IMPORTED_MODULE_4_.default is undefined and in vs code terminal
"export 'default' (imported as 'fb') was not found in '../../firebase'
and because of that neither user is getting registered nor the document is getting created
Does anyone know how to do this ?

Changing to
const fb = require('../../firebase.js');
or to
import { auth, usersCollection } from "../../firebase";
in the store file should do the trick.
In the first case, you need to also change
await fb.auth().createUserWithEmailAndPassword
to
await fb.auth.createUserWithEmailAndPassword
In the second case, change
await fb.auth().createUserWithEmailAndPassword
to
await auth.createUserWithEmailAndPassword
In this case, also note that if you would use database in the store/index.js file you would need to import database as well, like:
import { database, auth, usersCollection } from "../../firebase";
More explanations here and here.

Related

How to set useState by checking User data in Firebase

I can't seem to figure out how to set useState after querying Firebase.
I need BOX to be equal to "public URL" for everyone except for admin.
import { auth } from "../firebase";
...
const [BOX, setBox] = useState('');
useEffect(() => {
if (auth.currentUser?.email === "admin#yahoo.com") {
setBox("https://admin/link");
} else {
setBox("https://public/link");
}
},[auth]);
Errors are always different. Sometimes it returns "...empty link", sometimes "network request failed". Sometimes it does show the correct data, but it runs into one of the errors when I refresh the screen.
Here is my firebase file
// Import the functions you need from the SDKs you need
import * as firebase from "firebase";
import 'firebase/firestore';
// 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 = {
...
};
// Initialize Firebase
let app;
if (firebase.apps.length === 0) {
app = firebase.initializeApp(firebaseConfig);
} else {
app = firebase.app()
}
const auth = firebase.auth();
const dbFirebase = app.firestore();
export {auth, dbFirebase};
You can not use hook inside an if statement...
const [box, setBox] = useState('');
useEffect(() => {
if (auth.currentUser?.email === "admin#yahoo.com") {
setBox("https://admin/link");
} else {
setBox("https://public/link");
}
},[auth]);
For firebase 9 you will need to configure it like below:
import { initializeApp } from 'firebase/app'
import { getAuth } from 'firebase/auth'
const firebaseConfig = {...}
initializeApp(firebaseConfig)
export const auth = getAuth()

Firebase v9 Database. ref is not a function?

I've started migrating over to v9 Modular firebase. Please see my config:
import { initializeApp } from 'firebase/app';
import { getDatabase } from "firebase/database";
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
..
};
const app = initializeApp(firebaseConfig);
// Get a reference to the database service
export const database = getDatabase(app);
Then in another file I made basic CRUD functions.
import { database } from "./firebase"
export const insertTestData = () => {
return database.ref("test").set({name:"hello world"})
}
I got the following error:
Uncaught TypeError: _firebase__WEBPACK_IMPORTED_MODULE_0__.database.ref is not a function
Did I miss anything?
I'm not sure why it's also not picking up imports
You are importing ref() from Modular SDK but still using name-spaced syntax.
Try:
import { getDatabase, ref, set } from "firebase/database";
export const insertTestData = () => {
return set(ref(database, "test"), { name: "hello" })
}
Make sure you are referring to the "Modular" tab of the code snippets in the documentation

React Native: firestore/firebase Expected first argument to collection() to be a CollectionReference... how?

So I have a simple React Native app that I created using Expo and debug on my personal Android device.
I've included firebase/firestore into my app and am now trying to add an object to firestore on button click.
Here's my code:
firebaseConfig.js :
import { initializeApp } from '#firebase/app';
var config = {
...
}
const app = initializeApp(config);
export default app;
Component:
import { app } from '../firebaseConfig';
import { getFirestore } from 'firebase/firestore/lite';
import { doc, setDoc, collection, query, where, getDocs, initializeFirestore } from "firebase/firestore";
...
export default function Component() {
const firestoreDb = getFirestore(app);
// this function is called when clicking a button
const onAddListPress = () => {
setDoc(doc(firestoreDb, "cities", "LA"), {
name: "Los Angeles",
state: "CA",
country: "USA"
});
}
}
This throws the following error:
Expected first argument to collection() to be a CollectionReference, a DocumentReference or FirebaseFirestore
This code above (in the onPress) is copied from the official firestore docs, here:
https://firebase.google.com/docs/firestore/manage-data/add-data#set_a_document
Does anyone have an idea why this does not work?
I suggest you edit your post and remove the firebaseConfig configuration keys, or delete your Firebase app and create a new one from the Firebase console to secure your app. I tried to replicate your application using React, and I received the same error message. There were two changes I made to correct the error. The first one is inside the firebaseConfig file, the default export needs to be changed to a named export:
import { initializeApp } from "firebase/app"
const firebaseConfig = {
//Project configuration keys
};
const app = initializeApp(firebaseConfig);
export { app }; // Change from a default to a named export
In addition, all the imports inside your component should come from the full Javascript SDK, not the lite version. The lite version has some limitations which can result in this specific error.
import { app } from "./firestore-config";
import { getFirestore, getDoc, doc } from "firebase/firestore";
import './App.css';
export default function App() {
const firestoreDB = getFirestore(app);
const docRef = doc(firestoreDB, "users", "testUser1");
const getData = async () => {
const data = await getDoc(docRef);
console.log(data);
}
getData();
return <div className="App"></div>;
}
You might also want to review this GitHub issue if you are using yarn to manage your dependencies.

Retrieving Firebase Data in a config.js file for React Native Expo App

I've been developing a React Native Expo App which is compatible with both iOS and Android devices. In this app I need to retrieve and render data from my google firebase database.
I would like to rewrite my firebase code into a config.js file as currently I have the firebase script inside each App screen which is causing issues. Instead, I want to write a config.js file which exports the data, allowing me to retrieve the data in each screen by the import statement:
import { CustomerData } from '../app/config.js';
The code below (minus the export statement) works just fine on each screen when I use the 'info' data retrieved. However this is causing issue when adding new features to screens and is an inefficient way to retrieve google firebase data.
I've attempted to write the config.js file like so:
import "firebase/firestore";
import * as firebase from "firebase";
import React, { useState, Component, useEffect } from "react";
import "react-native-gesture-handler";
const firebaseConfig = {
apiKey: <My Api Key>,
authDomain: <My AuthDomain>,
projectId: <My Project Id>,
storageBucket: <My storage Bucket>,
messagingSenderId: <Sender Id>,
appId: <App ID>,
measurementId: <Measurement ID>,
};
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
} else {
firebase.app();
}
function GetData() {
const [info, setInfo] = useState([]); // Initial empty array of info
useEffect(() => {
const dbh = firebase.firestore();
dbh
.collection("CustomerDatabase")
.get()
.then((querySnapshot) => {
const info = [];
querySnapshot.forEach((documentSnapshot) => {
info.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
});
setInfo(info);
});
}, []);
export info;
I believe I need to add an export function which exports the retrieved data - 'info'. However I have attempted to place this export statement in different locations in the script and it returns the error
SyntaxError: 'import' and 'export' may only appear at the top level. (44:0)
Even when placing the export statement at the start of the script I still receive the same error. Also when removing the export state it throws the error:
TransformError SyntaxError: Unexpected token (44:15)
Indicating the last line as the culprit. This same code format works on the app screens however.
How would I export the 'info' data from within a config.js file? or do I have to stick to retrieving my firebase data from within each screens js file?
I ran this code, and except for the fact that you forgot to add a curly brace to close your GetData funtion, there appears to be no other error
import "firebase/firestore";
import * as firebase from "firebase";
import React, { useState, Component, useEffect } from "react";
import "react-native-gesture-handler";
const firebaseConfig = {
apiKey:"",
authDomain:"",
projectId:"",
storageBucket:"",
messagingSenderId:"",
appId:"",
measurementId:"",
};
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
} else {
firebase.app();
}
function GetData() {
const [info, setInfo] = useState([]); // Initial empty array of info
useEffect(() => {
const dbh = firebase.firestore();
dbh
.collection("CustomerDatabase")
.get()
.then((querySnapshot) => {
const info = [];
querySnapshot.forEach((documentSnapshot) => {
info.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
});
setInfo(info);
});
}, []);
}
export {info};
It makes sense though because info is not a global variable, you declared info as a variable in GetData, so if you want it to be accessible you should return it in your GetData() function. Then you can now export GetData and you should get back info from the function.
import "firebase/firestore";
import * as firebase from "firebase";
import React, { useState, Component, useEffect } from "react";
import "react-native-gesture-handler";
const firebaseConfig = {
apiKey:"",
authDomain:"",
projectId:"",
storageBucket:"",
messagingSenderId:"",
appId:"",
measurementId:"",
};
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
} else {
firebase.app();
}
function GetData() {
const [info, setInfo] = useState([]); // Initial empty array of info
useEffect(() => {
const dbh = firebase.firestore();
dbh.collection("CustomerDatabase").get().then((querySnapshot) => {
const info = [];
querySnapshot.forEach((documentSnapshot) => {
info.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
});
setInfo(info);
});
}, []);
return(info);
}
export {info};
//Then wherever you want to use info you can just do this...
import {GetData} from "filepath";
const info = GetData();
Another way I like to export state in my react apps is to define the state in my App.js so it's accessible to every component in my app. Example of how I would export a state holding the theme for my app below. I pass my state ad its setter as a prop to my AppRouting Component. And then whenever I have a component I want to have access to the theme in my AppRouting, I just do this: theme={props.theme} setTheme={props.setTheme}
import { useState } from "react";
import { Box, ThemeProvider, } from "#mui/material";
import { darkTheme } from "./Theming/AppTheme";
import AppRouting from "./Pages/AppRouting";
const App = () => {
const [theme, setTheme] = useState(darkTheme);
return(
<ThemeProvider theme={theme}>
<Box
sx={{
position:"absolute",
width:"100vw",
height:"auto",
minHeight:"100vh",
top:"0",
left:"0",
display:"flex",
backgroundColor:"background.default",
}}
>
<AppRouting
theme={theme}
setTheme={setTheme}
/>
</Box>
</ThemeProvider>
);
}
export default App;

How to use firebase auth and Cloud Firestore from different components as a single firebase App

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.

Categories