Reputation: 2229
I am trying to use firestore with my react project after authentication to write some data to firebase. But whenever doc.set
is called, I get an error saying Cannot read property firestore of null
.
Here is my firebase config file.
import firebase from 'firebase/app';
const 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,
};
if (firebase.apps.length === 0) {
console.log('Initializing firebase');
firebase.initializeApp(config);
}
export default firebase;
And here is my firebase utils file which I use to access the common requirements from firebase.
import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';
import {FIREBASE_COLLECTIONS} from './constants';
export const getIdToken = async () => {
try {
const {currentUser} = firebase.auth();
if (currentUser) {
const idToken = await currentUser.getIdToken();
return idToken;
}
return currentUser;
} catch (error) {
throw error || new Error('Error retrieving ID token');
}
};
export const logout = async () => {
try {
await firebase.auth().signOut();
} catch (error) {
throw error || new Error('Error logging out');
}
};
export const loginUser = async (email: string, password: string) => {
try {
const user = await firebase
.auth()
.signInWithEmailAndPassword(email, password);
return user;
} catch (error) {
let message = '';
switch (error.code) {
case 'auth/invalid-email':
message = 'Invalid Email Id';
break;
case 'auth/user-disabled':
message = 'User is temporarily disabled';
break;
case 'auth/user-not-found':
message = 'User not found. Please register';
break;
case 'auth/wrong-password':
message = 'Incorrect password';
break;
default:
message = 'Error logging in';
break;
}
throw new Error(message);
}
};
export const registerUser = async (email: string, password: string) => {
try {
const user = await firebase
.auth()
.createUserWithEmailAndPassword(email, password);
return user;
} catch (error) {
const errorCode = error.code;
let message = '';
switch (errorCode) {
case 'auth/email-already-in-use':
message = 'Email already in use';
break;
case 'auth/invalid-email':
message = 'Invalid email ID';
break;
case 'auth/weak-password':
message = 'Weak password';
break;
default:
message = 'Error registering user';
break;
}
throw new Error(message);
}
};
export const signInWithGoogle = async () => {
const provider = new firebase.auth.GoogleAuthProvider();
provider.setCustomParameters({prompt: 'select_account'});
const scopes = ['profile', 'email'];
scopes.forEach(scope => provider.addScope(scope));
try {
return await firebase.auth().signInWithPopup(provider);
} catch (error) {
let message = '';
switch (error.code) {
case 'auth/account-exists-with-different-credential':
message = 'Account exists with another sign in type';
break;
case 'auth/popup-closed-by-user':
message = 'Login popup closed';
break;
default:
message = 'Error signing in';
break;
}
throw new Error(message);
}
};
export const isLoggedIn = (): boolean => {
const {currentUser} = firebase.auth();
return currentUser !== null;
};
export const getUserDoc = (
id: string
): firebase.firestore.DocumentReference => {
return firebase
.firestore()
.collection(FIREBASE_COLLECTIONS.USERS)
.doc(id);
};
export default firebase;
I'm calling the getUserDoc function from the firebase utils and using it to write data to the user.
function* registerWithPassword(user: UserInput) {
try {
const userData: firebase.auth.UserCredential = yield call(
registerUser,
user.email,
user.password
);
if (userData === null || userData.user === null) {
yield put(
authFailureAction.failure({
register: 'Unable to register user',
})
);
} else {
const userDoc = getUserDoc(userData.user.uid);
yield call(
userDoc.set,
{labels: [], pinnedNotes: [], name: user.name},
{merge: true}
);
}
} catch (e) {
console.log({e});
yield put(
authFailureAction.failure({
register: e.message || 'Unable to register user',
})
);
}
}
When userDoc.set
is called, the error occurs.
From some debugging, it looks like firebase is null. That's why firebase.firestore()
throws an error.
But if firebase is null, auth shouldn't work too but I haven't had any problems with firebase auth.
Also note that I am importing firebaseConfig.js
in my index.js
file, so I'm sure that firebase has been initialized. This is also due to the fact that users are being registered but for some reason when I'm accessing firestore
from firebase.firestore()
, firebase
is null.
You can access the project here. Try to register a user and you'll see the error. Since it depends on Firebase, you'll have to add a relevant .env
file.
Upvotes: 1
Views: 765
Reputation: 1282
The error may be occurring because of the issue with the multiple firebase imports/exports done in files instead of a single module as a source.
The firebase
module could be exported only in firebase_config
file, and then be imported from there in other files.
So in firebase_utils
, you can add instead of:
import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';
import firebase from './firebase-config.js';
You can also remove export default firebase;
from firebase_utils
to prevent firebase
namespace from getting overwritten.
So the final firebase_config.js
file may look as:
// Firebase App (the core Firebase SDK)
var firebase = require("firebase/app");
// Add the Firebase products that you want to use
require("firebase/auth");
require("firebase/firestore");
// Your app's Firebase project configuration
var firebaseConfig = {
// ...
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
// Single source for all imports of the firebase module
export default firebase;
You can also take reference from this article and this GitHub repo for firebase
importing examples.
Upvotes: 2
Reputation: 2188
You have to use the firebase that has been initialized.
import firebase from 'firebase/app'; <-- This one hasn't been initialized.
...
firebase.initializeApp(firebaseConfig);
export default firebase; <-- This one is initialized.
In your firebase_config, add firestore, and auth
import firebase from 'firebase/app';
import 'firebase/auth'; <----- add this here.
import 'firebase/firestore'; <----- add this here.
const 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,
};
if (firebase.apps.length === 0) {
console.log('Initializing firebase');
firebase.initializeApp(config);
}
export default firebase;
Change the first 3 lines.
import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';
import {FIREBASE_COLLECTIONS} from './constants';
export const getIdToken = async () => {
try {
const {currentUser} = firebase.auth();
if (currentUser) {
const idToken = await currentUser.getIdToken();
....
to
import firebase from './firebase_config.js'; // or wherever your firebase.initializeApp(...) live.
import {FIREBASE_COLLECTIONS} from './constants';
export const getIdToken = async () => {
try {
const {currentUser} = firebase.auth();
if (currentUser) {
const idToken = await currentUser.getIdToken();
....
Good day.
Upvotes: 2