Search code examples
vue.jsvuejs3vue-routerpiniavue-router4

Vue 3 Composition Api , Vue-router 4 Naigation guards throw an Error when using it


When I try to use navigation guard from Vue-router 4, my code is no longer working it show a errors with undefined (push) from Pinia store router.push('/'). Here is my code:

import { createRouter, createWebHistory } from "vue-router";
import manageStaffRoutes from "./manage.staff.js";
import authRoutes from "./login.js";

import { useAuthStore } from "@/stores/auth.store.js";

const routes = [...manageStaffRoutes, ...authRoutes];

const router = createRouter({
    history: createWebHistory(import.meta.env.BASE_URL),
    routes,
});

router.beforeEach(async (to, from) => {
    const auth = await useAuthStore();
    const publicPages = ["/login", "/super-admin-login"];
    const authRequired = !publicPages.includes(to.path);
    if (authRequired && !auth.user) {
        return "/";
    }
});
export default router;

then in my store I have this code , where I am trying to redirect the user after successfully login

import { defineStore } from "pinia";
import { ref, computed } from "vue";
import axios from "axios";
import { useRouter } from "vue-router";

export const useAuthStore = defineStore("userAuth", () => {
    const router = useRouter();
    const userData = ref(null);
    const errorMessages = ref(null);

    const user = computed(() => {
        return JSON.parse(localStorage.getItem("user"));
    });

    if (user.value) {
        axios.defaults.headers.common["Authorization"] = `${user.value.token}`;
    }

    async function login(email, password) {
        try {
            const data = await axios.post("api/v1/users/login", {
                email: email,
                password: password,
            });
            if (data.status === 200) {
                userData.value = data.data;
                localStorage.setItem(
                    "user",
                    JSON.stringify(userData.value.data)
                );
                await router.push("/"); // the error
               
            }
        } catch (error) {
            console.log(error);
            return (errorMessages.value = error.response.data.errorsToSend);
        }
    }

    const logout = async () => {
        try {
            const data = await axios.post("api/v1/users/logout");
            console.log(data);
            if (data.status === 201) {
                user.value = null;
                localStorage.removeItem("user");
                await router.push({ name: "login" }); // the error
            
            }
        } catch (error) {
            console.log(error);
            // return (errorMessages.value = error.response.data.message);
        }
    };
    return { login, user, errorMessages, logout };
});

Here is the error: enter image description here


Solution

  • Following the single responsability principle I suggest you to change the login() function on the store, remove the router push to let the store only authenticate your user. By doing this the redirection is handled by the calling funcion an not by the login.

    AuthStore

    import { defineStore } from "pinia";
    import { ref, computed } from "vue";
    import axios from "axios";
    
    export const useAuthStore = defineStore("userAuth", () => {
    
        // .... 
    
        async function login(email, password) {
            try {
                const data = await axios.post("api/v1/users/login", {
                    email: email,
                    password: password,
                });
    
                if (data.status === 200) {
                    userData.value = data.data;
                    localStorage.setItem(
                        "user",
                        JSON.stringify(userData.value.data)
                    );
                }
            } catch (error) {
                console.log(error);
                return (errorMessages.value = error.response.data.errorsToSend);
            }
        }
    };
    

    Router

    import { useAuthStore } from "@/stores/auth.store.js";
    
    // ...
    
    router.beforeEach(async (to, from) => {
        const authStore = useAuthStore();
        const publicPages = ["/login", "/super-admin-login"];
        const authRequired = !publicPages.includes(to.path);
    
        if (authRequired && !auth.user) {
            return "/";
        }
    });