如何在Pinia Store中正确使用useFirebaseAuth?
在Pinia Store中集成VueFire与Firebase Authentication的问题
在Pinia Store中结合VueFire使用Firebase Authentication时,若在Store外部执行const auth = useFirebaseAuth()获取Auth实例,会触发警告:inject() can only be used inside setup() or functional components.;调用signInWithEmailAndPassword等Firebase Auth方法时,还会出现错误:Cannot read properties of undefined (reading 'app') at signInWithEmailAndPassword。
用户原代码如下:
import { defineStore } from 'pinia' import router from '../router/index' import { useFirebaseAuth } from 'vuefire' import { signOut, createUserWithEmailAndPassword, signInWithEmailAndPassword } from 'firebase/auth' const auth = useFirebaseAuth() export const useUserStore = defineStore('UserStore', { state: () => { return { user: {}, isLoggedIn: false, email: '', password: '', repassword: '', errorMessage: '', } }, actions: { async register() { if (this.password === this.repassword) { await createUserWithEmailAndPassword(auth, this.email, this.password) .then((userCredential) => { const user = userCredential.user console.log(user) router.push('/:placeName') }) .catch((error) => { const errorCode = error.code console.log(errorCode) this.errorMessage = error.message alert(this.errorMessage) }) } else { alert('Passwords must match') } }, async login() { await signInWithEmailAndPassword(auth, this.email, this.password) .then((userCredential) => { const user = userCredential.user console.log(user) router.push({ name: 'menu' }) }) .catch((error) => { const errorCode = error.code console.log(errorCode) this.errorMessage = error.message alert(this.errorMessage) }) }, async signout() { await signOut(auth) .then(() => { router.push({ name: 'login' }) }) .catch((error) => { const errorCode = error.code console.log(errorCode) this.errorMessage = error.message alert(this.errorMessage) }) } } })
用户提到将const auth = useFirebaseAuth()移至State中可正常运行,但不确定是否为正确实现方式。
正确实现方案
核心原因
useFirebaseAuth()依赖Vue的依赖注入系统,只能在组件的setup()函数、Pinia的setup()风格Store,或是Store的action内部调用,不能在Store外部的顶级作用域执行。
方案1:在每个Action内部获取Auth实例
将useFirebaseAuth()移至每个action内部,确保每次调用action时都能正确获取注入的Auth实例:
import { defineStore } from 'pinia' import router from '../router/index' import { useFirebaseAuth } from 'vuefire' import { signOut, createUserWithEmailAndPassword, signInWithEmailAndPassword } from 'firebase/auth' export const useUserStore = defineStore('UserStore', { state: () => { return { user: {}, isLoggedIn: false, email: '', password: '', repassword: '', errorMessage: '', } }, actions: { async register() { const auth = useFirebaseAuth() if (this.password === this.repassword) { try { const userCredential = await createUserWithEmailAndPassword(auth, this.email, this.password) const user = userCredential.user console.log(user) router.push('/:placeName') } catch (error) { console.log(error.code) this.errorMessage = error.message alert(this.errorMessage) } } else { alert('Passwords must match') } }, async login() { const auth = useFirebaseAuth() try { const userCredential = await signInWithEmailAndPassword(auth, this.email, this.password) const user = userCredential.user console.log(user) router.push({ name: 'menu' }) } catch (error) { console.log(error.code) this.errorMessage = error.message alert(this.errorMessage) } }, async signout() { const auth = useFirebaseAuth() try { await signOut(auth) router.push({ name: 'login' }) } catch (error) { console.log(error.code) this.errorMessage = error.message alert(this.errorMessage) } } } })
方案2:使用Pinia的Setup风格Store
改用Pinia的setup写法,在setup函数内部获取Auth实例,所有action可复用该实例,代码更简洁:
import { defineStore } from 'pinia' import { ref } from 'vue' import router from '../router/index' import { useFirebaseAuth } from 'vuefire' import { signOut, createUserWithEmailAndPassword, signInWithEmailAndPassword } from 'firebase/auth' export const useUserStore = defineStore('UserStore', () => { const auth = useFirebaseAuth() const user = ref({}) const isLoggedIn = ref(false) const email = ref('') const password = ref('') const repassword = ref('') const errorMessage = ref('') const register = async () => { if (password.value === repassword.value) { try { const userCredential = await createUserWithEmailAndPassword(auth, email.value, password.value) user.value = userCredential.user console.log(user.value) router.push('/:placeName') } catch (error) { console.log(error.code) errorMessage.value = error.message alert(errorMessage.value) } } else { alert('Passwords must match') } } const login = async () => { try { const userCredential = await signInWithEmailAndPassword(auth, email.value, password.value) user.value = userCredential.user console.log(user.value) router.push({ name: 'menu' }) } catch (error) { console.log(error.code) errorMessage.value = error.message alert(errorMessage.value) } } const signout = async () => { try { await signOut(auth) router.push({ name: 'login' }) } catch (error) { console.log(error.code) errorMessage.value = error.message alert(errorMessage.value) } } return { user, isLoggedIn, email, password, repassword, errorMessage, register, login, signout } })
关于将Auth放入State的疑问
把useFirebaseAuth()放到state里虽能运行,但不推荐:
- State应存储可序列化的应用状态数据,Auth实例是Firebase的工具对象,不属于状态范畴
- 这种写法依赖Pinia初始化的时机,存在注入失败的潜在风险,不符合Vue依赖注入的规范
内容的提问来源于stack exchange,提问作者Obed Monsalve
相关产品推荐
相关产品推荐

