简体   繁体   中英

How to handle firebase's onAuthStateChanged function in quasar

Currently I'm using tailwind css and headlessui for a few components and firebase.

Now I would like to use quasar but the boot files are very mysterious to me.

Currently I manage firebase with config.js, main.js and pinia store.

I replaced my old config.js file with a firebase.js boot file as recommended by Quasar and it seems to work. (but I don't really know if it's good practice)

import { boot } from 'quasar/wrappers'

import { initializeApp } from 'firebase/app'
import { getFirestore } from 'firebase/firestore'
import { getAuth } from 'firebase/auth'

const firebaseConfig = {
  apiKey: 'xxxxxxxxxxxxxx',
  authDomain: 'xxxxxxxxxxxxxx',
  projectId: 'xxxxxxxxxxxxxx',
  storageBucket: 'xxxxxxxxxxxxxx',
  messagingSenderId: 'xxxxxxxxxxxxxx',
  appId: '1:xxxxxxxxxxxxxx'
}

// Init firebase
initializeApp(firebaseConfig)

// Init services
const db = getFirestore()
const auth = getAuth()
export { db, auth }

// "async" is optional;
// more info on params: https://v2.quasar.dev/quasar-cli/boot-files
export default boot(async (/* { app, router, ... } */) => {
  // something to do
})

But I don't know what to do with the old mains.js file which is no longer available in Quasar. In main.js there is the following code:

import { createApp, markRaw } from 'vue'
import router from './router/router'
import { createPinia } from 'pinia'

import App from './App.vue'

// firebase
import { auth } from './firebase/config'
import { onAuthStateChanged } from 'firebase/auth'

import './input.pcss'

let app

onAuthStateChanged(auth, () => {
  if (!app) {
    app = createApp(App)
      .use(
        createPinia().use(({ store }) => {
          store.$router = markRaw(router)
        })
      )
      .use(router)
      .mount('#app')
  }
})

What should I do with the code above in particular with the onAuthStateChanged function?

Thanks for your help

I've found a solution for this that is suitable for my purposes. For me the requirements were:

  1. Make sure auth is initialized on a refresh, before rendering.
  2. Make sure any data required for the app is also initialized, before rendering.
  3. Detect log-in, log-out, and time outs and act accordingly.

I haven't tested time outs yet but basically I solved this with the following flow.

  1. In your router/index.js file, add a before each function, that checks to see if a listener is active, and calls a store function to create it if not.
 Router.beforeEach(async (to, from, next) => {
   // Access a store where you check if the auth changes are being handled
   const storeAuth = useAuth()
   if (!storeAuth.handlingAuth) {
     await storeAuth.handleAuth()
   }
   // Redirects as necessary using to.path and next
   next()
 })

  1. In the auth store, make a function that returns a promise to await in the beforeEach . Something like:
async handleAuth() {
 const auth = getAuth()
 return new Promise((resolve) => {
   let initialLoad = true
   auth.onAuthStateChanged(async (user) => {
     if (user) {
       await this.initializeUserData()
     } else {
       await this.clearUserData()
     }
     // If it is not initial load, use the router to push
     // depending on whether the user exists.
     // if (user && !initialLoad) this.router.push('/members')
     // This would detect a login and go to the members section.

     // If it is the initial load, resolve the promise
     // so the router proceeds
     if (initialLoad) {
       initialLoad = false
       this.handlingAuth = true
       resolve()
     }
   })
 })
}
  1. Don't make the mistake of using useRouter() in the store. useRouter() is only for use in the setup function, or <script setup> . What you need to do is add the router as a plugin to Pinia. So in your stores/index.js import your router, then add this:
pinia.use(({ store }) => { store.router = markRaw(router) })

That way you can use this.router.push() in your store modules.

This might seem a bit messy because of redirects in both the navigation guard and the store action but this seems like the easiest way to get it to load the required data from both refresh and login while only using onAuthStateChanged in one place.

In summary it works like this:

  1. When refreshed or entering a url manually, the app awaits the first state change, and any necessary loading.
  2. Then in the nav guard, you can check whatever store variables you need to check your user login state, and redirect. Like back to the login page, if the user happened to enter a url for a members-only section.
  3. Subsequent navigations (non-refresh) see that the auth handling is already set up, so it is ignored.
  4. When a separate function triggers login or logout, we can see that it is not the initialLoad, and do any other redirects at that point.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM