Skip to content

Instantly share code, notes, and snippets.

@gregfenton
Last active April 19, 2022 12:29
Show Gist options
  • Save gregfenton/6a780accca041229268bf44ca4024f71 to your computer and use it in GitHub Desktop.
Save gregfenton/6a780accca041229268bf44ca4024f71 to your computer and use it in GitHub Desktop.
App, FirebaseProvider and AuthProvider for use with v9 Firebase Web SDK

A super-simple React app for use with Firebase Authentication, Firestore and (optionally) other Firebase services.

This code uses React's Context API to make both Firebase services and Authentication objects (user, profile, etc.) available to the rest of the application.

To use:

  1. git clone ...
  2. npm install
  3. Add your Firebase project's values to firebaseConfig in FirebaseProvider.js
  4. npm run start

Things you might also try:

  • edit values in FirebaseProvider.js's useEffect() to enable use of the Firebase Emulator
import React from "react";
import {FirebaseProvider} from "contexts/FirebaseContext";
import {AuthProvider} from "contexts/AuthProvider";
const App = () => {
return (
<FirebaseProvider>
<AuthProvider>
<RestOfTheApp />
</AuthProvider>
</FirebaseProvider>
);
};
import React, {createContext, useContext, useEffect, useState} from 'react';
import {signInWithEmailAndPassword, signOut, onAuthStateChanged} from 'firebase/auth';
import {doc, onSnapshot} from 'firebase/firestore';
import {FirebaseContext} from './FirebaseProvider';
export const AuthContext = createContext({});
export const AuthProvider = (props) => {
const children = props.children;
const [user, setUser] = useState(null);
const [profile, setProfile] = useState(null);
const [authLoading, setAuthLoading] = useState(true);
const {myAuth, myFS} = useContext(FirebaseContext);
const loginFunction = async (email, password) => {
try {
let userCredential = await signInWithEmailAndPassword(myAuth, email, password);
let user = userCredential.user;
if (!user?.uid) {
let msg = `No UID found after signIn!`;
console.error(msg);
}
if (user) {
console.log(`Logged in as uid(${user.uid}) email(${user.email})`);
}
setUser(user);
} catch (ex) {
let msg = `Login failure for email(${email}: ${ex.message})`;
console.error(msg);
}
};
const logoutFunction = async () => {
try {
setUser(null); // shut down the listeners
await signOut(myAuth);
} catch (ex) {
console.error(ex);
}
};
// hook into Firebase Authentication
useEffect(() => {
let unsubscribe = onAuthStateChanged(myAuth, (user) => {
// if user is null, then we force them to login
if (user) {
setUser(user);
}
setAuthLoading(false);
});
return unsubscribe;
}, []);
// listen to the user profile (FS User doc)
useEffect(() => {
let unsubscribe = null;
const listenToUserDoc = async (uid) => {
try {
let docRef = doc(myFirestore, `users/${uid}`);
unsubscribe = await onSnapshot(docRef, docSnap => setProfile(docSnap.data()));
} catch(ex) {
console.error(`useEffect() failed with: ${ex.message}`);
}
};
if (user?.uid) {
listenToUserDoc(user.uid)
return () => {
unsubscribe && unsubscribe();
};
} else if (!user) {
setAuthLoading(true);
}
}, [user, setProfile]);
if (authLoading) {
return <h1>Loading</h1>;
}
const theValues = {
authLoading,
user,
profile,
login: loginFunction,
logout: logoutFunction,
};
return (
<AuthContext.Provider value={theValues}>
{children}
</AuthContext.Provider>
);
};
import React, {createContext, useEffect, useState} from 'react';
import {getAuth, connectAuthEmulator} from 'firebase/auth';
import {getFirestore, connectFirestoreEmulator, clearPersistence} from 'firebase/firestore';
import {getStorage, connectStorageEmulator} from 'firebase/storage';
export const FirebaseContext = createContext({});
const firebaseConfig = {
"apiKey": "",
"authDomain": "",
"databaseURL": "",
"projectId": "",
"storageBucket": "",
"messagingSenderId": "",
"appId": "",
"measurementId": ""
}
export const FirebaseProvider = (props) => {
const children = props.children;
const [firebaseInitializing, setFirebaseInitializing] = useState(true);
const [usingEmulators, setUsingEmulators] = useState(false);
const [emulatorsConfig, setEmulatorsConfig] = useState(false);
const myApp = initializeApp(firebaseConfig);
const myAuth = getAuth(myApp);
const myFS = getFirestore(myApp);
const myStorage = getStorage(myApp);
useEffect(() => {
const shouldUseEmulator = false; // or true :)
if (shouldUseEmulator) {
let mapEmulators = {};
let FS_HOST = "localhost";
let FS_PORT = 5002;
if (FS_HOST && FS_PORT) {
connectFirestoreEmulator(myFS, FS_HOST, FS_PORT);
console.log(`firestore().useEmulator(${FS_HOST}, ${FS_PORT})`);
mapEmulators.FS_HOST = FS_HOST;
mapEmulators.FS_PORT = FS_PORT;
}
let CLEAR_PERSISTENCE = false; // or true :)
if (CLEAR_PERSISTENCE) {
console.warn(`Calling firestore().clearPersistence() !!`);
clearPersistence(myFS);
mapEmulators.FIRESTORE_CLEAR_PERSISTENCE = CLEAR_PERSISTENCE;
}
let AUTH_HOST = "localhost";
let AUTH_PORT = 9099; // or whatever you set the port to in firebase.json
if (AUTH_HOST && AUTH_PORT) {
let AUTH_URL = `http://${AUTH_HOST}:${AUTH_PORT}`;
console.log(`connectAuthEmulator(${AUTH_URL}, {disableWarnings: true})`);
// warns you not to use any real credentials -- we don't need that noise :)
connectAuthEmulator(myAuth, AUTH_URL, {disableWarnings: true});
mapEmulators.AUTH_HOST = AUTH_HOST;
mapEmulators.AUTH_PORT = AUTH_PORT;
mapEmulators.AUTH_URL = AUTH_URL;
}
let STORAGE_HOST = "localhost";
let STORAGE_PORT = 5004; // or whatever you have it set to in firebase.json
if (STORAGE_HOST && STORAGE_PORT) {
console.log(`connectStorageEmulator(${STORAGE_HOST}, ${STORAGE_PORT})`);
connectStorageEmulator(myStorage, STORAGE_HOST, STORAGE_PORT);
mapEmulators.STORAGE_HOST = STORAGE_HOST;
mapEmulators.STORAGE_PORT = STORAGE_PORT;
}
setUsingEmulators(true);
setEmulatorsConfig(mapEmulators);
console.log(
'FIREBASE STARTUP: using Firebase emulator:',
JSON.stringify(mapEmulators, null, 2)
);
}
setFirebaseInitializing(false);
}, []);
if (firebaseInitializing) {
return <h1>Loading</h1>;
}
const theValues = {
usingEmulators,
emulatorsConfig,
myApp,
myAuth,
myFS,
myStorage
};
return (
<FirebaseContext.Provider value={theValues}>
{children}
</FirebaseContext.Provider>
);
};
import React, {useContext} from "react";
import {AuthContext} from './AuthProvider';
const RestOfTheApp = () => {
const {profile} = useContext(AuthContext);
if (!profile) {
return <LoginScreen/>
} else {
return (
<h1>Hello, {profile.displayName}</h1>
);
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment