Window is not defined while using firebase auth in next.js - javascript

Following code is from firebaseConfig.js:
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
import { getAuth } from "firebase/auth";
const firebaseConfig = {
//credentials//
};
export const app = initializeApp(firebaseConfig);
export const analytics=getAnalytics(app)
export const authentication=getAuth(app);
Following code is from index.js:
export default function Home() {
const auth = getAuth();
const generateRecaptcha=()=>{
window.recaptchaVerifier = new RecaptchaVerifier('recaptcha-container', {}, authentication);
}
window.recaptchaVerifier = new RecaptchaVerifier('recaptcha-container', {}, auth);
const getOTP=()=>{
generateRecaptcha()
}
I am getting error:
ReferenceError: window is not defined
After removing export getAnyalytics, I am still getting the same error but at window.recaptchaVerifier function in index.js.
Also please tell me the use of getAnalytics.

getAnalytics() will instantiate an instance of Firebase Analytics that you can use to log events throughout your app.
The solution for me when using analytics was to create a provider as follows:
FirebaseTrackingProvider.tsx
export const FirebaseTrackingProvider = (props: {children: ReactNode}) => {
const router = useRouter();
const [analytics, setAnalytics] = useState(null);
useEffect(() => {
setAnalytics(getAnalytics(firebaseApp));
if (analytics) {
setAnalyticsCollectionEnabled(analytics, true);
}
const handleRouteChange = (url: string) => {
if (!analytics) {
return;
}
logEvent(analytics, 'page_view', {
page_location: url,
page_title: document?.title,
});
setCurrentScreen(analytics, document.title ?? 'Undefined');
};
router.events.on('routeChangeStart', handleRouteChange);
return () => {
router.events.off('routeChangeStart', handleRouteChange);
};
}, [analytics, router.events]);
return <FirebaseContext.Provider value={analytics}>{props.children}</FirebaseContext.Provider>;
};
I can then consume it different pages or components:
const analytics = useContext(FirebaseContext);
// in sign up flow
logEvent(analytics, 'sign_up', {
uid: data.uid,
email: data.email,
});
Regarding the recapture erorr: NextJS will first attempt to render serverside content if there is any, before bootstrapping the react application. This means that the window has not been defined yet when you are trying to instantiate a new RecaptchaVerifier instance. You can use an if(window) to make sure you are only doing so when the window is instantiated, or alternatively, you can run a useEffect as follows:
useEfect(() => {
// This wont change on re renders
let completed = false;
if (!completed && window){
// recaptca instantiation
completed = true;
}
}, [window])

Related

Is there a way to make an equivalent of the onValue() firebase function in a JS class?

I'm currently developping a game's prototype with js and firebase
To avoid having to import all the functions of firebase on each file, I created a class gathering all the functions I need:
firebase.js:
import { initializeApp } from "firebase/app";
import { getDatabase } from "firebase/database";
import { getAuth } from "firebase/auth";
const firebaseConfig = { ... };
const app = initializeApp(firebaseConfig);
export const db = getDatabase();
export const auth = getAuth();
export default app;
services/DatabaseService.js:
import { db } from "../firebase";
import { ref, get, set, update, remove } from "firebase/database";
class DatabaseService {
get = async (_ref = "") => {
let output;
await get(ref(db, _ref)).then((snapshot) => {
output = snapshot.val();
});
return output;
}
create = async (_ref = "", _content) => {
await set(ref(db, _ref), _content);
};
update = async (_ref = "", _content) => {
await update(ref(db, _ref), _content);
};
remove = async (_ref = "") => {
await remove(ref(db, _ref));
};
}
const DB = new DatabaseService();
export default DB;
I make a little example for you
example.js :
import DB from "./services/DatabaseService";
const roomRef = "rooms/42534";
//Create a new room with name, owner and a list of player
await DB.create(roomRef, {
name: "NicolAme's Room",
owner: 0,
players: {
0: {
pseudo: "NicolAme",
score: 0
}
}
});
//Change the name of the room
await DB.update(roomRef, {
name: "Best Room"
});
//Log the room's content
console.log(await DB.get(roomRef));
//Delete the room after 10 seconds
setTimeout(async () => {
await DB.remove(roomRef);
}, 10000);
function App() {
return <>
<p>Sample</p>
</>
}
export default App;
So, here is my question :
I want to make an equivalent to the onValue() firebase function, which do a call each time the reference is updated. Is there a way to make a listener like this one in a class ?
Thanks in advance
This is what setters and getters are used for. Below is an example using classes, but object literals can also use the get and set keyword.
class ObserverableObject {
constructor(name,listener) {
console.log(listener);
this.name = name;
this._prop = null;
this._propUpdated = listener;
console.log(this);
}
get prop(){
return this._prop;
}
set prop(newProp){
//console.log(this,this._propUpdated);
this._propUpdated(newProp);
this._prop = newProp;
}
}
const obj = new ObserverableObject('test', (propValue) => {
console.log("prop changed to",propValue);
});
console.log(obj);
obj.prop = 2;
console.log(obj.prop);
obj.prop = 3;
console.log(obj.prop);
obj.prop = 4;
console.log(obj.prop);
Notice, how the listener is passed when object is created. This gives you flexibility to decide what you want to happen when the property is changed.
The property _prop cannot be named same as prop, because then you have multiple variables of same name. It is a convention to prefix _.
You can use something similar for onValue

React Function to Component and Setting State with Function Return

here's the jist of where I'm stuck (or just read the title for my question).
I have a firebase.js file where I have functions to authenticate. signinGithub, signinGoogle, signinEmail and so forth. The Firebase Auth business logic is in these functions.
I am showing errors with console.log or alert from these functions. The functions are imported into a Component and I don't know how to capture the functions result into the component by somehow setting state from this out-of-component function file.
Here's a basic example:
firebase.js
...
const signInWithGitHub = async () => {
try {
const res = await signInWithPopup(auth, githubProvider)
const user = res.user
} catch (err) {
alert(err) // ** I want to pass "err" from here to Login
// ** component by updating Logins state for a message
}
}
export {signinWithGitHub}
...
Login.jsx
import React, { useEffect, useState } from "react"
import { useAuthState } from "react-firebase-hooks/auth"
import {
auth,
signInWithGitHub
} from "../lib/firebase"
function Login() {
const [user, loading, error] = useAuthState(auth)
render(
{* Below is the method call from the imported custom firebase function *}
<button onClick={signInWithGitHub}>
Login with GitHub
</button>
)
}
...
I was thinking something like this but I can't fully resolve it in my mind:
Set state in Login.js const [message, setMessage] = useState('')
When the imported signinWithGitHub has an error message --
I'm stuck figuring out how to apply to function message to the state, any ideas?
You can create a custom function inside your Login. jsx file to call the original signInWithGitHub method with a try catch block. And more importantly, you should not use render inside a functional component. Use return to render the JSX in DOM.
firebase.js
export const signInWithGitHub = async () => {
try {
const res = await signInWithPopup(auth, githubProvider);
const user = res.user;
} catch (err) {
throw new Error(err?.message || "Unable to sign in with GitHub");
}
};
Login.jsx
import React, { useEffect, useState } from "react";
import { useAuthState } from "react-firebase-hooks/auth";
import { auth, signInWithGitHub } from "../lib/firebase";
function Login() {
const [user, loading, error] = useAuthState(auth);
const [errorMessage, setErrorMessage] = useState("");
const onLogin = async () => {
try {
await signInWithGitHub();
} catch (err) {
setErrorMessage(err);
}
};
return (
<>
<button onClick={onLogin}>Login with GitHub</button>
{!!errorMessage && <h5>{errorMessage}</h5>}
</>
);
}

React Native: TypeError: navigationRef.isReady is not a function. (In 'navigationRef.isReady()'

in my react-native app App.js I setup a listener for firebase notifications, I also setup navigation in order to navigate and get current name inside notif handler functions, however when my app mounts I get this error:
TypeError: navigationRef.isReady is not a function. (In 'navigationRef.isReady()', 'navigationRef.isReady' is undefined)
getCurrentRoute
In my App.js file
import { navigationRef, isReadyRef, getCurrentRoute, navigate } from '#env/RootNavigation.js';
useEffect(() =>
{
console.log('APP STARTED');
registerListener();
}, []);
const registerListener = () =>
{
messaging().onMessage((message) => {
handleForegroundNotification(message);
});
};
const handleForegroundNotification = (message) =>
{
let routeName = getCurrentRoute();
Alert.alert(routeName);
};
<NavigationContainer ref={navigationRef} onReady={ () => { isReadyRef.current = true; } }>
<ROOTSTACK1></ROOTSTACK1>
</NavigationContainer>
In my RootNavigation file:
import * as React from 'react';
export const isReadyRef = React.createRef();
export const navigationRef = React.createRef();
export function navigate(name, params) {
if (isReadyRef.current && navigationRef.current) {
// Perform navigation if the app has mounted
navigationRef.current.navigate(name, params);
} else {
// You can decide what to do if the app hasn't mounted
// You can ignore this, or add these actions to a queue you can call later
}
};
export function getCurrentRoute(){
if (navigationRef.isReady()) {
const route=navigationRef.getCurrentRoute();
console.log(route);
// sample output {key:"Home-k2PN5aWMZSKq-6TnLUQNE",name:"Home"}
return route.name;
}
};
Documentation states to use createNavigationContainerRef vs React.createRef.
So it would be
export const navigationRef = createNavigationContainerRef();
from https://reactnavigation.org/docs/navigating-without-navigation-prop

How to Stub a module in Cypress

I'm trying to stub a module using Cypress. Here's what I've tried so far, but is not working.
This is the short version of my component/page
// SomeComponent.jsx
import { useSomething } from './useSomething'
const SomeComponent = () => {
// useSomething is a custom hook
const { data, error } = useSomething()
const renderData = () => {
// map the data into an array of JSX elements
return data.map(...)
}
return (
<div>
{renderData()}
</div>
)
}
export default SomeComponent
Here's how my custom hook looks like
// useSomething.js
import { useState } from 'react'
import { getData } from './db'
export const useSomething = () => {
const [data, setData] = useState({})
const [error, setError] = useState()
useEffect(() => {
getData().then(data => {
setData(data)
}).catch(err => {
setError(error)
})
// ... some other unrelated code here
}, [])
return { data, error }
}
Here's how getData looks like
// getData.js
export const getData = () => {
const data = // some API call from external services
return data
}
The method is exposed via db.js (actually db/index.js)
// db.js
export * from './getData'
I'm trying to stub the getData.js to make the e2e test more consistent. Here's what I did.
// something.spec.js
// I'm writing #src just to keep the code sample here short, it's the same file as the db.js I write above
import * as db from '#src/db'
...
// this is how I try to do the stubbing
cy.stub(db, 'getData').resolves(something)
...
The stubbing above doesn't work. The API call to the external service is still happening when running the test. The documentation itself leads me to deduce that I should write it this way, but it's not working.
You can expose db on the window
// useSomething.js
import { useState } from 'react'
import * as db from './db'
const { getData } = db;
if (window.Cypress) { // only when testing
window.db = db;
}
and in the test
cy.window().then(win => {
cy.stub(win.db, 'getData').resolves(something);
})
Or use intercept to stub the API call.

React Native Firebase Authentication with Hooks loses userState on app resume

I'm currently creating a React Native mobile application with Typescript.
The application uses the Firebase authentication with the Google OAuth Provider.
In order to use the username and some other details (retrieved from Firestore) I'm using a React Provider like shown in the following example:
import React, {useState, useEffect} from 'react';
import auth from '#react-native-firebase/auth';
import { GoogleSignin } from '#react-native-community/google-signin';
import firestore from '#react-native-firebase/firestore';
GoogleSignin.configure({
webClientId: 'x.googleusercontent.com',
});
const getUserById = async (id: string) => {
const admin = await firestore().collection("users").doc(id).collection("priv").doc("admin").get();
const prot = await firestore().collection("users").doc(id).collection("priv").doc("protected").get();
const jsonData = {
admin: admin.data(),
protected: prot.data(),
};
return jsonData;
}
const AuthContext = React.createContext({});
function AuthProvider(props: any) {
const [user, setUser] = useState(auth().currentUser);
const [details, setDetails] = useState({});
const [initializing, setInitializing] = useState(true);
const onAuthStateChanged = async (authUser: any) => {
setUser(authUser);
if (authUser !== null)
refreshDetails();
}
const refreshDetails = async () => {
const details = (await getUserById(user.uid));
setDetails(details);
}
useEffect(() => {
const subscriber = auth().onAuthStateChanged(onAuthStateChanged);
return subscriber; // unsubscribe on unmount
}, []);
const loginWithGoogle = async () => {
const { idToken } = await GoogleSignin.signIn();
// Create a Google credential with the token
const googleCredential = auth.GoogleAuthProvider.credential(idToken);
// Sign-in the user with the credential
return auth().signInWithCredential(googleCredential);
}
const logout = () => {
auth()
.signOut()
}
return (
<AuthContext.Provider value={{user, loginWithGoogle, logout, refreshDetails, details, initializing}} {...props}></AuthContext.Provider>
)
}
const useAuth = () => {
const state = React.useContext(AuthContext);
return {
...state,
};
}
export {AuthProvider, useAuth};
As you can see in the example I'm using this useEffect method from React to subscribe to authentication changes.
Unfortunately if I close the app and reopen it again, this authentication change isn't triggered so the user state isn't set and I get a bunch of errors.
What would be the best practice in a scenario like this? I think I only need to trigger the onAuthStateChangeEvent when the app was started again.
Thanks for all help
IJustDev
onAuthStateChanged function must be triggered when the app re-opens. However, it's supposed to run asynchronously you have to implement the case user's value is invalid.

Categories