I can't firebase v9 enableIndexedDbPersistence in a nextjs pwa.
this erros are trow
index.js // firebase main
import { initializeApp } from 'firebase/app'
import { getAuth } from 'firebase/auth'
import { getFirestore } from 'firebase/firestore'
import { getStorage } from 'firebase/storage'
const firebaseConfig =
process.env.NEXT_PUBLIC_FIREBASE_CONFIG
const app = initializeApp(JSON.parse(firebaseConfig))
export const db = getFirestore(app)
// enableIndexedDbPersistence(db) // >>> if put here it said can't be invoked after getFirestore or any other funtion
export const auth = getAuth(app)
export const storage = getStorage(app)
So where should i invoke
The enableIndexedDbPersistence(db) function must be called before . From its documentation:
Must be called before any other functions (other than
{#link initializeFirestore}, {#link (getFirestore:1)} or
{#link clearIndexedDbPersistence}.
So getFirestore(app) is actually exempted from that, in contrast what you stated in the comment of your code snipped:
// >>> if put here it said can't be invoked after getFirestore or any other function
Consequently, my guess would be that you might use the exported db before the promise of enableIndexedDbPersistence(db) completes.
You could resolve this in different ways, such as wrapping it in a service or method and make sure you are await-ing the promise, or generally change your app that db is not used right away.
In my Ionic PWA app, I'm successfully using the following:
import { getFirestore, enableIndexedDbPersistence } from "firebase/firestore";
import { initializeApp } from "firebase/app";
const firebaseConfig = {
// ...
};
const app = initializeApp(firebaseConfig);
export const db = getFirestore(app);
enableIndexedDbPersistence(db)
.then(() => console.log("Enabled offline persistence"))
.catch((error) => {
if (error.code == "failed-precondition") {
// Multiple tabs open, persistence can only be enabled
// in one tab at a a time.
// ...
} else if (error.code == "unimplemented") {
// The current browser does not support all of the
// features required to enable persistence
// ...
}
});
This is very similar to your snipped indeed. But the first access to Firestore happens after via user interaction, and not right away.
Related
I'm developing a REACT JS app and I've a doubt. If I have something that must not be taken as a dependency i usually put a let like in this case, where I'm initializing Firebase for my app:
import { initializeApp } from 'firebase/app';
import { initializeAppCheck, ReCaptchaV3Provider } from 'firebase/app-check';
let app = null;
const useInitializeFirebase = () => {
if (app) {
return;
}
app = initializeApp(window.FIREBASE);
initializeAppCheck(app, {
provider: new ReCaptchaV3Provider(window.RECAPTCHA_SITE_KEY),
isTokenAutoRefreshEnabled: true,
});
};
export default useInitializeFirebase;
The so exported hook is called within the main index.js file in this way:
const App = () => {
useInitializeFirebase();
...
}
So if the hook gets called multiple times (e.g. for a re-render), Firebase won't give error.
Is this the best approach? Or maybe I should use useRef or useState or even something else?
EDIT
The problem occurs also if I don't use an hook.
Take this code as reference:
import { initializeApp } from 'firebase/app';
let firebaseApp = null;
export default (firebaseConfig) => {
if (firebaseApp === null) {
firebaseApp = initializeApp(firebaseConfig);
}
return firebaseApp;
};
That can be used in this way
import { getAuth, signInWithCustomToken } from 'firebase/auth';
import firebaseApp from '#Src/helpers/firebaseApp';
const firebaseLogin = (token) => {
const auth = getAuth(firebaseApp(window.FIREBASE));
return signInWithCustomToken(auth, token);
};
export { firebaseLogin };
In this way if I don't put a guard firebase would be initialized multiple times, giving error.
The firebase docs show them using the app instance inside the getAuth() call, in one portion of the docs
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
const firebaseConfig = {
// ...
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app); //I'm talking about this line
Then in the very next section on the same page, they go ahead and use getAuth() without the app instance, in order to sign in a user with email.
import { getAuth, createUserWithEmailAndPassword } from "firebase/auth";
const auth = getAuth();
createUserWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
const user = userCredential.user;
})
.catch((error) => {
console.log(error)
});
What is the correct way to use it?
The getAuth() function without parameters will use the default initialized instance of Firebase. If you have multiple instances of Firebase i.e. multiple projects, then you must pass the app instance to specify which project's auth that auth instance must use.
const app1 = initializeApp(project1Config);
const app2 = initializeApp(project2Config, "app2");
const auth1 = getAuth(); // or getAuth(app1), uses app1 as it's default,
const auth2 = getAuth(app2); // uses app2
I am using firebase web sdk without React. I have 4 pages in my application. I am using express js in for each pages to send html file of that route.
I have a file called common.js in which I defined methods that will be shared by all pages. In the same file I defined a method to act on auth changed is as following:
Modified below code
import { initializeApp } from 'firebase/app';
import { getAuth, onAuthStateChanged } from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';
import { firebaseConfig } from './firebase.config';
const firebaseApp = initializeApp(firebaseConfig);
const db = getFirestore(firebaseApp);
const auth = getAuth(firebaseApp);
const addNavbar = () => {
$('#nav').load('nav.html', () => {
document.getElementById('btnSignOut').addEventListener('click', (e) => {
auth.signOut();
});
});
}
// createAlert, createEditDeleteButtons, displayLoaderAndHideApp defined here
const unsubscribeOnAuthStateChanged = onAuthStateChanged(auth, async (user) => {
if (user) {
window.location.replace('/home');
} else {
window.location.replace('/login');
}
});
export {
addNavbar, auth, createAlert, createEditDeleteButtons, db, displayLoaderAndHideApp,
};
On each page I first call handleOnAuthStateChanged(), and it is also called after successful login. I want to persist the auth so I came across setPersistence(), so I used it in login.js and is as follows:
import { browserLocalPersistence, browserSessionPersistence, setPersistence, signInWithEmailAndPassword } from 'firebase/auth';
import { auth, handleOnAuthStateChanged } from './common';
document.getElementById('btnSignin').addEventListener('click', async (e) => {
e.preventDefault();
try {
const form = document.querySelector('form.formLogin');
const email = form.email.value.trim();
const password = form.password.value;
await setPersistence(auth, browserLocalPersistence);
await signInWithEmailAndPassword(auth, email, password);
handleOnAuthStateChanged(auth.currentUser); // here auth will retrieve the new logged in user
} catch (e) {
console.error(e);
}
});
If I use onAuthStateChanged() on other pages my user is null then I am stuck in a loop.
Am I missing something?
Update
All of the minimal code is after removing all the functionalities except a few that might causing the problem.
I have a file called nav.html that is loaded into page by common.js and is as defined above.
Code for home, page 1 and page 2 adds navigation by calling addNav. Home will import methods from common.js, page 2's js, and page 1's js. Home page's js is as following:
import { addDoc, collection, deleteDoc, doc, getDocs, onSnapshot, query, setDoc, where } from 'firebase/firestore';
import { addNavbar, createAlert, createEditDeleteButtons, db, displayLoaderAndHideApp as displayLoader } from './common';
// imports from page 1 and page 2
addNavbar();
// Other methods are defined here...
// Nothing is exported as of now..
Firebase automatically persists the auth state on most browser environments, and restores it on app/page reload. To detect this restore of auth state (and other auth state changes) you will need to register an auth state listener with the SDK as shown in the first code fragment in the documentation on getting the current user:
import { getAuth, onAuthStateChanged } from "firebase/auth";
const auth = getAuth();
onAuthStateChanged(auth, (user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
const uid = user.uid;
// ...
} else {
// User is signed out
// ...
}
});
Most of the code of your handleOnAuthStateChanged can probably go into this handler.
I'm using Google firebase v9. On the first sign in, usually there would be a pop up for user to choose which email to sigh in with, but for some reason, when I sign in, it automatically send me to the chat page automatically. I tried delete user in my firebase app and tried again and get the same result. What am I doing wrong?
firebase.js:
import { GoogleAuthProvider } from 'firebase/auth';
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
const firebaseConfig = {//myap config};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const provider = new GoogleAuthProvider();
export { auth, provider };
login-page:
import { auth, provider } from '../firebase';
import { useAuthState } from 'react-firebase-hooks/auth';
import { useNavigate } from 'react-router-dom';
import { signInWithPopup } from 'firebase/auth';
function Header(){
const [user] = useAuthState(auth);
const navigate = useNavigate();
const signIn = (e) => {
signInWithPopup(auth, provider)
.then(() => navigate('/channel'))
.catch((error) => alert(error.message));
};
return (
<buttononClick={!user ? signIn : () => navigate('/channel')}>Login</button/>
);
};
Maybe It's because I am already sign-in on chrome? If someone can check my code and verified, that would be awesome.
It sounds like your app is already associated with your Google account. Signing out does not sever that association.
Removing that association is described in the Remove third-party account access, but mostly consists of going to the list of Third-party apps with account access and removing your app from that list.
If that doesn't do it, have a look at the results on forcing the account picker to show up.
Any and all help would be appreciated! I continue to get this and other errors involving firestore() not being function, etc. I'm not sure how to fix this, I created a to-do react app with firebase and had no issues and a week later I'm receiving errors on any firebase project I create.
I've tried doing import firebase from 'firebase/compat/app' with other imports for firestore, auth, and storage with no luck. I've also tried importing initializeApp from 'firebase/app', although I'm not 100% sure if I've done that correctly.
Currently my firebase.js file looks like:
import firebase from 'firebase';
const firebaseApp = firebase.initializeApp({
...
});
const db = firebaseApp.firestore();
const auth = firebase.auth();
const storage = firebase.storage();
export { db, auth, storage };
I am doing import { db } from './firebase' in my app.js file.
If you have v9.0.0+ installed, then change your import to compat version:
import firebase from 'firebase/compat/app';
// import "firebase/compat/[SERVICE_NAME]"
I'd recommend checking out the new Modular SDK which has certain performance benefits. You can checkout this Firecast to learn more about the new SDK. The new syntax looks like:
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';
import { getStorage } from 'firebase/storage';
const firebaseApp = initializeApp({
...
});
const db = getFirestore(firebaseApp);
const auth = getAuth(firebaseApp);
const storage = getStorage(firebaseApp);
export { db, auth, storage };