ComponentDidMount I want to add fetch API and FirebaseAuth - firebase

I want to connect firebase stuff. What I'm trying in my news feed is when I press addTofavourite its name should go to firebase. So in my code for fetch, I used componentDidMount and for sending too there is componentDidMount. I have no idea how to connect them.
This is what I tried, but it's not working.
componentDidMount() {
firebase.auth().signInWithEmailAndPassword
("web#imandy.ie", "123456")
//////////////////////////////////////
this.fetchNews();
}
for fetching news
componentDidMount() {
this.fetchNews();
}
for firebase thing
componentDidMount() {
firebase.auth().signInWithEmailAndPassword("web#imandy.ie", "123456" )
}

Please try like this
componentDidMount = async () => {
const { user } = await firebase.auth().signInWithEmailAndPassword("web#imandy.ie", "123456");
// If you want to use user detail, write code here
// ...
this.fetchNews();
}

you have to call this function in componentWillReciveProps(){} or componentDidUpdate(){}

Related

Firebase Authentication JS does not populate `providerData`array

in a VueJS / QuasarJS application Im using firebase-js-sdk [1] together with firebaseui-web [2] to handle authentication.
After successful auth with any of the configured providers (e.g. password, google, apple, etc) I want to check which provider the user used. But immediately after successful authentication the user.providerData[] array that should contain the information is empty.
BUT if I reload my app the user.providerData[] array is suddenly populated correctly.
Iยดm checking for user data with something like this
import { getAuth } from "firebase/auth";
const auth = getAuth();
const user = auth.currentUser;
if (user) {
console.log(user.providerData)
}
After that the user object is fully populated (incl auth tokens, etc) but the user.providerData[] array is empty. Only after a page reload (CTRL-R) does the array get populated.
I searched both projects issues pages and documentation and didnt find anything that could explain this.
Im thankful for ANY idea where to look next!
EDIT
As suggested by #aside Im using onAuthStateChanged to check for updates of the user state.
onAuthStateChanged(
fbAuth,
(user) => {
if (user) {
console.log("onAuthStateChanged: user found");
console.log("onAuthStateChanged: user.providerData", user.providerData);
console.log("onAuthStateChanged: user", user);
} else {
console.log("onAuthStateChanged: no user found");
}
},
function (error) {
console.log("onAuthStateChanged:", error);
}
);
But even if I wait minutes after authentication is completed, still the user.providerData array is only populated after a page reload.
Here is a full demo: https://codesandbox.io/s/github/perelin/firebase-auth-providerdata-test
Thanks in advance :)
Im using
"firebase": "9.6.1",
"firebaseui": "6.0.0",
[1] https://github.com/firebase/firebase-js-sdk
[2] https://github.com/firebase/firebaseui-web
Your app should call getAuth().currentUser.reload() to refresh the local user data after login.
This could be done either in beforeRouteEnter() nav guard of the LoggedIn view:
// LoggedIn.vue
import { getAuth, signOut } from "firebase/auth";
export default {
async beforeRouteEnter(to, from, next) {
await getAuth().currentUser?.reload() ๐Ÿ‘ˆ
next()
},
}
demo 1
Or in the onAuthStateChanged callback:
// main.js
onAuthStateChanged(
fbAuth,
async (user) => {
await user?.reload() ๐Ÿ‘ˆ
},
)
demo 2
Your code is only running once instead of running every time the auth state is updated.
If you want to listen to any changes to the auth state, use a callback along with onAuthStateChanged as described here.
https://firebase.google.com/docs/auth/web/manage-users#get_the_currently_signed-in_user
import { getAuth, onAuthStateChanged } from "firebase/auth";
const auth = getAuth();
onAuthStateChanged(auth, (user) => {
if (user) {
// Check used provider here
const providerData = user.providerData;
// ...
} else {
// User is signed out
// ...
}
});
The reason checking/requesting the user object right after authentication does not work is that it might take firebase a second to update the providerData array. signInWithX might therefore return before the property is updated.

Nuxtjs and Firebase Auth: await firebase.auth().currentUser not waiting?

Nuxt.js is focuses on server side rendering and has an asyncData property that is called once before the page component is loaded.
I am trying something like:
async asyncData({params}) {
// firebase.auth().onAuthStateChanged((user)=>{ // <-- this doesn't work in the asyncData property
let user = await firebase.auth().currentUser
let info = {}
console.log(user)
user.uid === null // true
}
Two similar questions:
firebase.auth().currentUser is null
Get firebase.auth().currentUser with async/await
have solutions which do not seem to work with nuxt...
I have also tried:
function getCurrentUser(auth) {
let userLoaded = false;
return new Promise((resolve, reject) => {
if (userLoaded) {
resolve(firebase.auth().currentUser);
}
const unsubscribe = auth.onAuthStateChanged(user => {
userLoaded = true;
unsubscribe();
resolve(user);
}, reject);
});
}
It seems like onAuthStateChanged is triggered on client-side only. But the thing is, SSR functionality would make sense only for non-authenticated users, for authed-user scenario might as well just put the firebase call logic into mounted hook.

What is the proper way of connecting firebase with redux-sagas?

I am building react-native app, where I use react-native-firebase and redux-saga. This is my first project using redux-saga, hence I am learning. So far I got the authentication of the user using the following:
import firebase from 'react-native-firebase';
function* loginEmlPwdSaga(action) {
try {
const auth = firebase.auth();
const data = yield call(
[auth, auth.signInAndRetrieveDataWithEmailAndPassword],
action.email,
action.password,
);
yield put(loginSuccess(data));
} catch (error) {
yield put(loginFail(error));
}
}
However, now I am stuck at the point, where I want to subscribe to the collection. In react-native-firebase I would use the following:
firebase.firestore().collection('users').onSnapshot(...)
I am not sure what is the best approach to handle subscribes in redux-saga. Can you show me the pattern that I can use for the future usage of onSnapshots (testable one)?
The easiest way to subscribe to a firestore collection using redux-saga is to use a channel:
function * syncUsers () {
const ref = app.firestore().collection('users')
const channel = eventChannel(emit => ref.onSnapshot(emit))
try {
while (true) {
const data = yield take(channel)
yield put(successAction(data))
}
} catch (err) {
yield put(errorAction(err))
}
}
This will dispatch an action (created by successAction(data)) every time the firebase pushes a change to your client.
You can also use a library like redux-saga-firebase to automate this part and simply use:
function * syncUsers () {
yield fork(rsf.firestore.syncCollection, 'users', {
successActionCreator: successAction,
failureActionCreator: errorAction
})
}
Disclaimer: I'm the author of redux-saga-firebase.
UPDATE: redux-saga-firebase is no longer maintained and is not compatible with Firebase 8.

ionic2 - angularfire2 - firestore: Missing or insufficient permissions on logout

i'm stuck on this issue working on a ionic2 project with "firestore" service from firebase.
I have an osservable to get some data from firestore in a template using the async pipe.
Rule on this EndPoint give read and write access only to logged user.
When i sign-out i put a redirect to login page.
..and now come the issue..
when i land in the login page, after a few second, jump out the IonicErrorHandler notifying that i have insufficient permission.
so;
how i can tell to firestore osservable;
"hey dude, stop it, i call u later if someone log-in again"
(ill try an unsubscribe() befour the signOut but not work, and also
it does not from persistence)
Recapping:
when i logOut
this.afAuth.auth.signOut();
the error:
core.es5.js:1020 ERROR Error: Missing or insufficient permissions.
at new FirestoreError (error.js:164)
at JsonProtoSerializer.fromRpcStatus (serializer.js:126)
at JsonProtoSerializer.fromWatchChange (serializer.js:517)
at PersistentListenStream.onMessage (persistent_stream.js:334)
at persistent_stream.js:270
at persistent_stream.js:247
at async_queue.js:81
at t.invoke (polyfills.js:3)
at Object.onInvoke (core.es5.js:3890)
at t.invoke (polyfills.js:3)
(to be precise, i recive it 3 times. Exactly the number or of documents in the collection)
Service where i call the firestore endpoint:
export interface Attivita {
id: string;
committente: string;
durata: number;
nome: string;
progetto: string;
userId: string;
}
#Injectable()
export class FirebaseService {
attivitaCollectionRef: AngularFirestoreCollection<Attivita>;
attivita$: Observable<Attivita[]>;
constructor(private afs: AngularFirestore,
public afAuth: AngularFireAuth ) {
}
setOsservableAttivita(uId){
this.attivitaCollectionRef = this.afs.collection('attivita', ref => {
return ref.where("userId", "==", uId)
});
this.attivita$ = this.attivitaCollectionRef.snapshotChanges().map(actions => {
return actions.map(action => {
console.log(action)
const data = action.payload.doc.data() as Attivita;
const id = action.payload.doc.id;
return { id, ...data };
});
});
}
}
tks in advance to all help me to understand it
:)
I'd recommend watching the authState from Firebase and only taking from snapshotChanges while you're authenticated. The switchMap operator allows you to switch between observables based on conditions such as whether or not the user is authenticated. Here is an example of a possible solution.
// Assuming rxjs 5.5.0 with lettable operators
import { map } from 'rxjs/operators/map';
import { switchMap } from 'rxjs/operators/switchMap';
import { empty } from 'rxjs/observable/empty';
import { create } from 'rxjs/observable/create';
const actions$ = this.attivitaCollectionRef.snapshotChanges()
.map(actions => {
return actions.map(action => {
console.log(action)
const data = action.payload.doc.data() as Attivita;
const id = action.payload.doc.id;
return { id, ...data };
});
});
}),
this.attivita$ = create(
// Listen for changes in the authState
subscriber => this.afAuth.onAuthStateChanged(subscriber))
)
.pipe(
// Determine if the user is logged in
map(user => !!user),
// switchMap will unsubscribe from the previous observable
// so when isLoggedIn switches to false actions$ will be unsubscribed from
switchMap(isLoggedIn => isLoggedIn ? actions$ : empty()),
);
calling this after logout solved it for me:
afStore.firestore.disableNetwork();
Yes,
Normally this is solved by doing an unsubscribe of your subscription in the ngOnDestroy() of the component you are navigating away from.
That's the way I do it.
So for you, that would be:
ngOnDestroy() {
this.attivita$.unsubscribe();
}
However, it is very difficult to pinpoint which you should unsubscribe as the error does not give any indication on that.
I have added a question to the devs of angularFire on your issue:
https://github.com/angular/angularfire2/issues/1459.
It would be nice to have helped so that the exception points you in the right direction, for example, the path which was not unsubscribed or the last path segment.
Also, there are alternative methods for doing this listed in this post:
http://brianflove.com/2016/12/11/anguar-2-unsubscribe-observables/
Hope that helps.

Using Firebase user UID with vuefire manual binding

In a simple SPA with Vue and Firebase, there are two routes: Login and Chat.
Upon login, the user is redirected to the Chat route where Firebase database bindings are done manually using vuefire's $bindAsArray(), inside the created() life-cycle hook. This is because the bindings require the uid assigned by the Firebase authentication to be available.
This works fine, until the user refreshes the page. If auth().currentUser is used to get the uid, it returns null. If the auth().onAuthStateChanged() watcher is used, Vue attempts to render the component before the Firebase database bindings are done. What am I missing?
I come across this scenario, as workaround I use component wrapper that has UID as property, if UID is null show a waiting message/animation else show your original component.
My real scenario is a little more complex to post it here (firebase, routing, vuex) but basically that wrapper component should look similar to this
<template>
<component :is="CurrentComponent" />
</template>
<script>
import App from './App';
import WaitingAnimation from './WaitingAnimation';
export default {
data() {
return {
Uid: null,
}
},
computed: {
CurrentComponent() {
return this.Uid == null ? WaitingAnimation : App;
}
}
beforeMount() {
//While Firebase is initializing `Firebase.auth().currentUser` will be null
let currentUser = Firebase.auth().currentUser;
//Check currentUser in case you previously initialize Firebase somewhere else
if (currentUser) {
//if currentUser is ready we just finish here
this.Uid = currentUser.uid;
} else {
// if currentUser isn't ready we need to listen for changes
// onAuthStateChanged takes a functions as callback and also return a function
// to stop listening for changes
let authListenerUnsuscribe = Firebase.auth().onAuthStateChanged(user => {
//onAuthStateChanged can pass null when logout
if (user) {
this.Uid = user.uid;
authListenerUnsuscribe(); //Stop listening for changes
}
});
}
}
}
</script>

Resources