What does unsubscribe() do in this firebase auth sample? - javascript

On this page https://firebase.google.com/docs/auth/web/google-signin#expandable-2 there is a code sample where a function calls itself (see text in bold below) -- I'm confused what this means, can anybody please shed some light -- why does unsubscribe call itself?
console.log('Google Auth Response', googleUser);
// We need to register an Observer on Firebase Auth to make sure auth is initialized.
var unsubscribe = firebase.auth().onAuthStateChanged((firebaseUser) => {
unsubscribe();

Calling onAuthStateChanged() "adds an observer/listener for changes to the user's sign-in state" AND returns an unsubscribe function that can be called to cancel the listener.
So the goal of the following pattern
var unsubscribe = firebase.auth().onAuthStateChanged((firebaseUser) => {
unsubscribe();
// ...
});
is to listen only once for changes to the user's sign-in state. The first time the listener is triggered it calls the unsubscribe function, so it doesn't listen anymore.
As explained in the comment in the code you refer to, they use this pattern in order to make sure auth is initialized.
Note that one could have very well done:
var foo = firebase.auth().onAuthStateChanged((firebaseUser) => {
foo();
// ...
});

Related

Organizing multiple firebase triggers

I have multiple firebase triggers organized as follows:
Function1.ts:
exports.fileupload = db.collection("/x").onSnapshot(async (snap) => {
snap.docChanges().forEach((change) => {//something})
});
Function2.ts:
exports.something = db.collection("/y").onSnapshot(async (snap) => {
snap.docChanges().forEach((change) => {//something})
});
Then in index.ts:
const ai = require("./Function1");
const users = require("./Function2");
exports.fileupload = ai.fileupload ;
exports.something = users.something;
This causes the Function1.ts function to trigger multiple times when a new document is added. If I don't export them from index.ts as well then it triggers only once but then any firebase auth functions don't trigger at all. Is there a better way to organise this?
I understand that triggers can trigger multiple times for the same event, but I also can't find the eventId for onSnapshot as specified here
What causes this?
I don't see any database triggers in your code at all. You have two snapshot query listeners defined, but they are not triggers as you see in the documentation. If you had a Firestore trigger, it would look like this:
const functions = require('firebase-functions');
exports.myFunction = functions.firestore
.document('my-collection/{docId}')
.onWrite((change, context) => { /* ... */ });
If you define snapshot listeners at the top level of your code, they will execute exactly once for each server instance that is allocated to run any other actual trigger that happens. Any number of server instances can be allocated, based on the load applied to your function.
Since we can't see any of the code that actually defines the trigger, we can't fully understand what's going on here. But it's generally safe to say that long-running snapshot listeners are not appropriate for Cloud Functions code, which is stateless in nature. If you want to query the database in a trigger, you should use a get() instead of a listener.

Authentication with Firebase and context API in react give me a warning. Is this the right approach?

I am trying to set up an authentication on my web application in React using firebase and Context API.
I am using Context API since as long as I understood I cannot save my jwt token in local storage in order to not be vulnerable to XSS attack and at the moment I do not want to use Redux.
in my App.js I have:
const {setUserInfo} = useContext(userInfoContext);
useEffect(() => {
auth.onAuthStateChanged(user => {
if (user) {
setUserInfo({jwtToken: user.za});
} else {
setUserInfo({jwtToken: null});
}
console.log(user);
});
}, [setUserInfo]);
The methos "auth.onAuthStateChanged" is triggered every time I logged in or I logged out using firebase.auth.
The compiler tell me that to eliminate the warning I should have "[setUserInfo]" instead of "[]". However, doing as he say, the method setUserInfo is executed twice. There is a better way to achieve the result without a warning?
Your problem is that you don't clean up your effect when it is recomputed. As soon as you add setUserInfo to the dependency array, the effect is executed whenever its value changes. This means that you could potentially register many auth.onAuthStateChanged if the value of setUserInfo changes.
auth.onAuthStateChanged returns an unsubscribe function. You can simply return this function inside your effect, which will make react execute the unsubscribe function whenever the hook is executed again and prevent you from having multiple active listeners. I suggest you read more about this topic here.

remove listener for: firebase.auth().onIdTokenChanged

I listen for token being refresh with:
firebase.auth().onIdTokenChanged(async function (user) {....})
However, I just realized I don't delete the listener when required. How can I do this?
Should I use a different method to add listener? I don't understand exactly what is described here: https://firebase.google.com/docs/reference/android/com/google/firebase/auth/FirebaseAuth.IdTokenListener
When you call onIdTokenChanged it returns a method that you can use to unsubscribe the listener.
So capture it with something like:
let unsubscribe = firebase.auth().onIdTokenChanged(function(user) {
if (user) {
// User is signed in or token was refreshed.
}
});
And then you can unsubscribe the listener with:
unsubscribe();
Also see the reference documentation for onIdTokenChanged().

Meteor - Display the Email Address of the Logged In User

I am trying to display the email address of the logged in user with Meteor.
I am using the command Meteor.user().emails[0].address -- and this works sometimes only. Other times it is undefined. This is because sometimes the page renders before the User's collections is available.
However, I am using React and not blaze. Every solution online suggests using Meteor.subscribe() in the onCreated part of the template. But I cannot figure out React's equivalent and I cannot figure out how to wait for the User collection before rendering.
Updated to use Meteor.autorun which accepts a callback function that runs whenever Meteor's reactive sources update.
Meteor.subscribe accepts an onReady optional callback. I would attach to the componentWillMount lifecycle event on your React component, setup your meteor subscription, and cause a state change once onReady has fired. Here is some rough example code;
var Foo = React.createClass({
componentWillMount: function() {
var _this = this;
// Setup meteor subscription
Meteor.autorun(function () {
_this.setState({
user: Meteor.user(),
});
})
},
render: function() {
// Render nothing until we have a user
if (!this.state || !this.state.user) {
return null;
}
// Render the address when we have the user
return (
<div>{this.state.user.emails[0].address}</div>
);
}
});
Relevant docs: http://docs.meteor.com/api/pubsub.html#Meteor-subscribe

How to avoid dispatching in the middle of a dispatch

Within my Flux architected React application I am retrieving data from a store, and would like to create an action to request that information if it does not exist. However I am running into an error where the dispatcher is already dispatching.
My desired code is something like:
getAll: function(options) {
options = options || {};
var key = JSON.stringify(options);
var ratings = _data.ratings[key];
if (!ratings) {
RatingActions.fetchAll(options);
}
return ratings || [];
}
However intermittently fails when the dispatcher is already dispatching an action, with the message Invariant Violation: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.. I am often making requests in response to a change in application state (eg date range). My component where I make the request, in response to a change event from the AppStore has the following:
getStateFromStores: function() {
var dateOptions = {
startDate: AppStore.getStartISOString(),
endDate: AppStore.getEndISOString()
};
return {
ratings: RatingStore.getAll(dateOptions),
};
},
I am aware that event chaining is a Flux antipattern, but I am unsure what architecture is better for retrieving data when it does not yet exist. Currently I am using this terrible hack:
getAll: function(options) {
options = options || {};
var key = JSON.stringify(options);
var ratings = _data.ratings[key];
if (!ratings) {
setTimeout(function() {
if (!RatingActions.dispatcher.isDispatching()) {
RatingActions.fetchAll(options);
}
}, 0);
}
return ratings || [];
},
What would be a better architecture, that avoids event chaining or the dispatcher error? Is this really event chaining? I just want to change the data based on the parameters the application has set.
Thanks!
You can use Flux waitFor() function instead of a setTimeout
For example you have 2 stores registered to the same dispatcher and have one store waitFor the other store to process the action first then the one waiting can update after and dispatch the change event. See Flux docs example
My particular error was occurring because my stores emitted their change event during the action dispatch, while it was still cycling through the listeners. This meant any listeners (ie components) that then triggered an action due to a data change in the store would interrupt the dispatch. I fixed it by emitting the change event after the dispatch had completed.
So this:
this.emit(CHANGE_EVENT);
Became
var self = this;
setTimeout(function() { // Run after dispatcher has finished
self.emit(CHANGE_EVENT);
}, 0);
Still a little hacky (will probably rewrite so doesn't require a setTimeout). Open to solutions that address the architectural problem, rather than this implementation detail.
The reason you get a dispatch in the middle of a previous dispatch, is that your store dispatches an action (invokes an action creator) synchronously in the handler for another action. The dispatcher is technically dispatching until all its registered callbacks have been executed. So, if you dispatch a new action from either of the registered callbacks, you'll get that error.
However, if you do some async work, e.g. make an ajax request, you can still dispatch an action in the ajax callbacks, or the async callback generally. This works, because as soon as the async function has been invoked, it per definition immediately continues the execution of the function and puts the callback on the event queue.
As pointed out by Amida and in the comments of that answer, it's a matter of choice whether to make ajax requests from the store in response to an action, or whether to do it in the store. The key is that a store should only mutate its state in response to an action, not in an ajax/async callback.
In your particular case, this would be exemplified by something like this for your store's registered callback, if you prefer to make the ajax calls from the store:
onGetAll: function(options) {
// ...do some work
request(ajaxOptions) // example for some promise-based ajax lib
.then(function(data) {
getAllSuccessAction(data); // run after dispatch
})
.error(function(data) {
getAllFailedAction(data); // run after dispatch
});
// this will be immediately run during getAllAction dispatch
return this.state[options];
},
onGetAllSuccess: function(data) {
// update state or something and then trigger change event, or whatever
},
onGetAllFailed: function(data) {
// handle failure somehow
}
Or you can just put the ajax call in your action creator and dispatch the "success/failed" actions from there.
you can user the "defer" option in the dispatcher.
In your case it would be like:
RatingActions.fetchAll.defer(options);
In my case, I fetch data through the actions/actions creators. The store is only a dump place that receives the payload of an action.
This means that I would "fetchall" in an action and then pass the result to the store which will do whatever with it and then emit a change event.
Some people consider using stores like me, others think like you.
Some people at Facebook uses "my" approach:
https://github.com/facebook/flux/blob/19a24975462234ddc583ad740354e115c20b881d/examples/flux-chat/js/utils/ChatWebAPIUtils.js#L51
I think it would probably avoid the dispatch problem treating your stores like this, but I may be wrong.
An interesting discussion is this one: https://groups.google.com/forum/#!topic/reactjs/jBPHH4Q-8Sc
where Jing Chen (Facebook engineer) explains what she thinks about how to use stores.

Categories

Resources