sveltefire
sveltefire copied to clipboard
Loading state for user
First of good job, this is a fantastic library!
Now to the feature request: It would be great to have a loading or empty state for the User as well. Just looks a little funny jumping very quickly from the login to whatever page the user wants to see.
I hear you.
The problem is that Firebase does not broadcast a loading state, so it's not as simple as it would seem. https://stackoverflow.com/a/50684239/3808414
One solution (already implemented) is to save the current user to sessionStorage. If the user is logged in, they will not see the flash of the login on a full refresh.
<User persist={sessionStorage}>
But I am open to other ideas, so we can leave this issue open...
Finally found some time to look at the code of the lib.
How about introducing a variable like in the docStore? And then just reassign it once the data arrived. Maybe a we would need a timeout as well in case it's not loading.
let _loading = true;
const store = writable(cached, () => {
const teardown = auth.onAuthStateChanged(u => {
set(u);
persist && opts.persist.setItem(k, JSON.stringify(u));
_loading = false;
});
return () => teardown;
});
const { subscribe, set } = store;
return {
subscribe,
auth,
get loading() {
return _loading;
},
};
Otherwise the sessionStorage works perfectly fine. In most cases the user is probably not logged in anyway if the sessionStorage is empty.
It is also possible to handle the Authentication with a server session cookie. Then you can have SSR on the user data. The user data will then be shown as soon as the page is rendered. It requires you to keep a __session cookie on the server based on the login details. When you log a user in, you send the token to the server. The server validates it and saves it as the __session cookie (note the double underscore before session. That is if you want to host your server on Firebase as this is the only cookie they allow). Then you can pass the user data to sapper and use it in your SSR. This video explains how to do it Sveltecasts SSR-auth
Firebase does not broadcast a loading state
Why not just have cached = undefined
to start with (overwritten if persisted value exists) instead of null?
I always treated my internal user state variable as "ternary"
undefined
= loading
null
= user logged out
user object = logged in
Also is readable
store more appropriate if end user is not writing to it?
https://svelte.dev/repl/68476b7019874b5dbdfba8b8b4f0c3a7?version=3.18.2
I replaced sveltefire's User component with something radically simpler, and loading states work great:
<script>
import { getContext, createEventDispatcher } from "svelte";
const firebase = getContext('firebase').getFirebase();
const dispatch = createEventDispatcher();
let user
firebase.auth().onAuthStateChanged((u) => {
user = u
dispatch("user", { user })
})
</script>
{#if user === undefined}
<slot name="loading" />
{:else if user !== null}
<slot {user} auth={firebase.auth()} />
{:else}
<slot name="signed-out" />
{/if}
Things I ripped out:
- persisting into sessionStore (I'm ok with taking the hit of that one request if user refreshes the page, for now; would be pretty easy to add back)
- the whole concept of a separate store, just one
let user
is fine - going through the
assertApp
helper (because it's not exported, and I didn't feel the need for louder messages on programmer error) - the weird
before
andafter
slots; those can be done where this component is used
EDIT: It does not unsubscribe from firebase events yet. Should be pretty easy.
And this one should get unsubscribe right and also uses https://firebase.google.com/docs/reference/js/firebase.auth.Auth#currentuser if it is set.
EDIT: also dispatch event after currentUser trick.
<script>
import { getContext, createEventDispatcher, onMount } from "svelte";
const firebase = getContext('firebase').getFirebase();
const dispatch = createEventDispatcher();
let user
onMount(() => {
const auth = firebase.auth()
if (auth.currentUser !== null) {
user = auth.currentUser
dispatch("user", { user })
}
const unsubscribe = auth.onAuthStateChanged((u) => {
user = u
dispatch("user", { user })
})
return unsubscribe
})
</script>
{#if user === undefined}
<slot name="loading" />
{:else if user !== null}
<slot {user} auth={firebase.auth()} />
{:else}
<slot name="signed-out" />
{/if}
I hear you.
The problem is that Firebase does not broadcast a loading state, so it's not as simple as it would seem. https://stackoverflow.com/a/50684239/3808414
One solution (already implemented) is to save the current user to sessionStorage. If the user is logged in, they will not see the flash of the login on a full refresh.
<User persist={sessionStorage}>
But I am open to other ideas, so we can leave this issue open...
Hey it seems like this has been removed in subsequent releases? I'm very new to Svelte and web development in general and looking for a solution to this issue's problem that's as out-of-the-box as possible :)
Any update here? I would love this feature!
Refreshing the page flashes the "logged out" view for a split second in the current version, would love a solution for a seamless UX!