RangeError: Maximum call stack size exceeded when trying to log out - vue.js

I'm making a simple app using Vue.js and vue router. I'm getting this error when trying to log out of my app:
vue-router.esm.js?8c4f:2827 Uncaught (in promise) RangeError: Maximum call stack size exceeded
at RegExp.exec (<anonymous>)
at RegExp.[Symbol.match] (<anonymous>)
at String.match (<anonymous>)
at matchRoute (vue-router.esm.js?8c4f:1636)
at match (vue-router.esm.js?8c4f:1518)
at redirect (vue-router.esm.js?8c4f:1578)
at _createRoute (vue-router.esm.js?8c4f:1617)
at match (vue-router.esm.js?8c4f:1519)
at redirect (vue-router.esm.js?8c4f:1578)
at _createRoute (vue-router.esm.js?8c4f:1617)
replace # vue-router.esm.js?8c4f:2827
logout # App.vue?234e:64
click # App.vue?4de4:34
invokeWithErrorHandling # vue.runtime.esm.js?2b0e:1854
invoker # vue.runtime.esm.js?2b0e:2179
original._wrapper # vue.runtime.esm.js?2b0e:6917
3
Here's my router.js:
import Vue from "vue";
import Router from "vue-router";
import Home from "./views/Home.vue";
import Login from "./views/Login.vue";
import auth from "./auth";
Vue.use(Router);
export default new Router({
mode: "history",
base: process.env.BASE_URL,
routes: [
{
path: "/dashboard",
name: "home",
component: Home,
beforeEnter: requireAuth
},
{
path: "/",
name: "login",
component: Login,
redirect: () => {
if (auth.loggedIn()) return "/dashboard";
else return "/";
}
},
{
path: "/users",
name: "users",
component: () => import("./views/Users.vue"),
beforeEnter: requireAuth
},
{
path: "/plans",
name: "plans",
component: () => import("./views/Plans.vue"),
beforeEnter: requireAuth
},
{
path: "/meetups",
name: "meetups",
component: () => import("./views/Meetups.vue"),
beforeEnter: requireAuth
},
{
path: "/notif",
name: "notifications",
component: () => import("./views/Notifications.vue"),
beforeEnter: requireAuth
},
{
path: "/admins",
name: "admins",
component: () => import("./views/Admins.vue"),
beforeEnter: requireAuth
},
{
path: "/about",
name: "about",
component: () => import("./views/About.vue"),
beforeEnter: requireAuth
}
]
});
function requireAuth(to, from, next) {
if (!auth.loggedIn()) {
next({
path: "/login",
query: { redirect: to.fullPath }
});
} else {
next();
}
}
Here's my logout function:
logout() {
auth.logout();
this.$router.replace("/");
}
The app is a simple dashboard for showing some stats. I'm using a custom auth using firebase etch. It was working earlier I don't know what i did to breakt it pls help!
Update I get another error when using code suggested in error, not sure what this error is about:
TypeError: Cannot read property 'name' of null
at Proxy.render (eval at ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"3092cd10-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/App.vue?vue&type=template&id=7ba5bd90& (app.js:1116), <anonymous>:30:55)
at VueComponent.Vue._render (vue.runtime.esm.js?2b0e:3548)
at VueComponent.updateComponent (vue.runtime.esm.js?2b0e:4066)
at Watcher.get (vue.runtime.esm.js?2b0e:4479)
at new Watcher (vue.runtime.esm.js?2b0e:4468)
at mountComponent (vue.runtime.esm.js?2b0e:4073)
at VueComponent.Vue.$mount (vue.runtime.esm.js?2b0e:8415)
at init (vue.runtime.esm.js?2b0e:3118)
at createComponent (vue.runtime.esm.js?2b0e:5978)
at createElm (vue.runtime.esm.js?2b0e:5925)

This is because when you logged out, auth.loggedIn() will return false.
So in redirect property of login route, else part execute. And you redirect to login page again, and then redirect execute again and again. and in an infinite loop you are redirecting to login page.
You have to check and not run redirect function if you are redirecting to login page. You can use beforeEnter like other routes:
function checkLogin(to, from, next) {
if (auth.loggedIn()) {
next({
path: "/dashboard",
});
} else {
next();
}
}
and in routes array:
routes: [
...
{
path: "/",
name: "login",
component: Login,
beforeEnter: checkLogin
},
...
]

Related

Vue router - navigation guard is skipped when using router.push()

I have a Vue SPA with i18n and some views that require authentication via navigation guard.
When i am not authenticated and go to url via my browser:
examplepage.com/en/lockedpage
i get redirected to:
examplepage.com/en/login
which is good, however when i click a button that runs:
#click="$router.push(`/${$i18n.locale}/lockedpage`)"
i get in to the page even if i am not authenticated.
I want to get redirected to the login page if not authenticated
this is my router.js:
import Vue from 'vue';
import Router from 'vue-router';
import Home2 from './views/Home2.vue';
import Login from './views/Login.vue';
import Register from './views/Register.vue';
import ErrorLanding from './views/ErrorLanding.vue'
import Root from "./Root"
import i18n, { loadLocaleMessagesAsync } from "#/i18n"
import {
setDocumentLang
} from "#/util/i18n/document"
Vue.use(Router);
const { locale } = i18n
export const router = new Router({
mode: 'history',
base: '/',
routes: [
{
path: '/',
redirect: locale
},
{
path: "/:locale",
component: Root,
children: [
{
name: 'Home',
path: '',
component: Home2,
},
{
name: 'Home2',
path: '/',
component: Home2,
},
{
name: 'Login',
path: 'login',
component: Login,
},
{
path: 'register',
component: Register,
},
{
path: 'lockedpage',
name: 'lockedpage',
webpackChunkName: "lockedpage",
meta: {authRequired: true},
component: () => import('./views/LockedPage.vue')
},
{
path: '*',
component: ErrorLanding,
name: 'NotFound'
}
]
}
],
router.beforeEach((to, from, next) => {
if (to.params.locale === from.params.locale) {
next()
return
}
const { locale } = to.params
loadLocaleMessagesAsync(locale).then(() => {
setDocumentLang(locale)
const publicPages = [ `/`, `/${locale}`, `/${locale}/`];
const authRequired = !publicPages.includes(to.path);
const loggedIn = localStorage.getItem('user');
const redirect = to.path;
if (authRequired && loggedIn === null) {
if(to.meta.authRequired === false) {
next();
}
else
next({ name: 'Login', query: { redirect: redirect } });
} else {
next();
}
})
next()
});
Why does my navigationguard skip when using router.push() ?
This issue started after adding i18n, with localeredirect. so all routes comes after a locale for example: /en/.../
As Estus points out in the comments, the issue is that the first thing you're checking for is if the locales match, and if they do you're calling next() and sending the user to the page.
As outlined here:
Make sure that the next function is called exactly once in any given pass through the navigation guard. It can appear more than once, but only if the logical paths have no overlap, otherwise the hook will never be resolved or produce errors.
If you need to keep the locale check between the to and from pages, you could do something like:
if (to.params.locale === from.params.locale && loggedIn) {
next()
return
}
which will check if the loggedIn variable is truthy before pushing the user to the page they're trying to navigate to.
I believe just removing the if statement that checks if the locales match would also work.

Navigating to route returns Maximum call stack size exceeded

I want to navigate the user to /home once he clicks on a box, but currently the redirection does not work and I get this error:
Maximum call stack size exceeded
What is causing this error and how can I fix it?
here is my relevant html:
<router-link to="/home"> <md-button class="view-captable-btn" v-for="item in capTables" :key="item.contractAddress" v-model="capTables" #click="getCapTableId(item.contractAddress, item); getCaptableName(item.label, item)" >
<p>{{item.label}}</p>
<p class="contract-address-p"> {{item.contractAddress}}</p>
</md-button>
</router-link>
my method:
getCapTableId(contractAddress, item) {
let address = item.contractAddress
store.commit('saveCapTableAddress', address)
},
and my protected route logic :
import Vue from 'vue'
import Router from 'vue-router'
import firebase from "firebase/app"
import "firebase/auth"
Vue.use(Router)
const router = new Router({
routes: [
{
path: '/',
name: 'Login',
component: Login
},
{
path: '/home',
name: 'Home',
component: Home
},
{
path: '/unauthorized',
name: 'Unauthorized',
component: Unauthorized
},
{
path: '/activity-log',
component: ActivityLog,
name: 'ActivityLog'
},
{
path: '/captables',
name: 'CapTables',
component: CapTables,
meta: {
requiresAuth: true
},
},
{
path: '*',
component: NotFound,
name: 'NotFound',
},
],
mode: 'history'
})
router.beforeEach((to, from, next) => {
const currentUser = firebase.auth().currentUser
const requiresAuth = to.matched.some(record => record.meta.requiresAuth)
if(requiresAuth && !currentUser) next('/unauthorized')
else if (!requiresAuth && currentUser) next('home')
else next()
});
export default router
I tried using pushing the route with this.$route.push('/home') still doesn' work.

How can I fix 'Unhandled Promise Rejection' for my Vue application?

For my Vue application I encounter the following issue in IE: 'Unhandled Promise Rejection undefined'.
I tracked the issue down to the authentication of the application. But I'm not sure how to tackle it. I've tried try-catch blocks around the next() functions.
Below the code of the authentication.
import router from '#/config/Router';
import CONST from '#/utils/Constants';
import ObjectHelper from "#/helpers/ObjectHelper";
class Auth {
constructor() {
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!Auth.isLoggedIn()) {
next({path: CONST.ROUTE.SESSION.LOGIN});
} else {
next();
}
} else {
next();
}
});
}
static logOut() {
localStorage.clear();
sessionStorage.clear();
router.replace(CONST.ROUTE.SESSION.LOGIN);
}
static isLoggedIn() {
return ObjectHelper.exists(localStorage.getItem(CONST.KEY.AUTH.ACCESS_TOKEN));
}
}
export default Auth;
After this.$router.push(CONST.ROUTE.ORGANISATIONS.OVERVIEW); in my login-component, the error is thrown.
To be complete, I also provide the code for the Router:
import Vue from 'vue';
import Router from 'vue-router';
Vue.use(Router);
export default new Router({
base: process.env.BASE_URL,
mode: 'history',
routes: [
{
path: '/',
name: 'home',
component: () => import('#/views/home/home.vue')
},{
path: '/session',
name: 'session',
component: () => import('#/views/session/session.vue'),
children: [
{
path: 'login',
name: 'login',
component: () => import('#/views/session/login/login.vue')
},
{
path: 'logout',
name: 'logout',
component: () => import('#/views/session/logout/logout.vue')
}
]
},
{
path: '/organisations',
alias: '/organisaties',
component: () => import('#/views/organisations/organisations.vue'),
children: [
{
path: '',
name: 'organisations-overview',
component: () => import('#/views/organisations/overview/overview.vue'),
meta: {
requiresAuth: true,
can: 'edit-organisations',
fail: '/session/logout'
}
},
{
path: ':uuid',
name: 'organisation-edit',
component: () => import('#/views/organisations/edit/edit.vue'),
meta: {
requiresAuth: true,
can: 'edit-organisations',
fail: '/session/logout'
}
}
]
}
]
});
Anyone an idea how to tackle this? Many thanks!
I found a solution for 'Unhandled Promise Rejection undefined' in IE. I needed to add support for IE11 to my Vue project: 'babel-polyfill', 'weakmap-polyfill', 'core-js/es/set', 'core-js/es/map'. After this, everything works fine.

Infinite loop with vue router beforeEach and children paths

When i use beforeEach with children paths debug console show this error: vue-router.esm.js?8c4f:2079 RangeError: Maximum call stack size exceeded
import Vue from 'vue'
import VueRouter from 'vue-router'
import LoginMixin from '#/mixins/LoginMixin.js'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: require('#/views/Home.vue').default,
},
{
path: '/login',
name: 'login',
meta: { layout: 'centered' },
component: () => import('#/views/Login.vue'),
},
{
path: '/register',
name: 'register',
meta: { layout: 'centered' },
component: () => import('#/views/Register.vue'),
children: [
{
path: 'user',
component: () => import('#/components/RegisterForm.vue'),
},
{
path: 'company',
component: () => import('#/components/CompanyForm.vue'),
}
]
},
]
//creamos la instancia router modo history(urls amigables)
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
router.beforeEach((to, from, next) => {
if (to.path != '/login' || to.path != '/register/user' && LoginMixin.methods.loginMixinCheckAuth() == false) {
//if not logead and join to other page distinc of login or register redirect to login
next('/login')
}
else {
next()
}
})
I dont know what is bad, the syntaxis is fine and function LoginMixin.methods.loginMixinCheckAuth() is working good (i tested without the function and result is the same).
Hmm at first glance I'd try to make this convoluted if in your beforeEach method simpler. Try to add something like requiresAuth: true to the meta of all your routes that require a logged in user.
In a sense you want something like this in your routes:
// ...
{
path: '/users/:userId(\\d+)/edit/',
name: 'EditUser'
props: true,
meta: {
requiresAuth: true, // <-- add this meta flag against which you check later in beforeEach
},
component: () => import(/* webpackChunkName: "user-edit" */ '#/views/UserEdit.vue'),
},
// ...
And this in your beforeEach:
router.beforeEach((to, from, next) => {
if (to.matched.some((record) => record.meta.requiresAuth)) { // <-- check for requiresAuth here
// assuming your login mixin works
// if I were you I'd store some JWT in localStorage and check that somehow in a vuex getter
if (!LoginMixin.methods.loginMixinCheckAuth()) {
next('/login')
} else {
next()
}
} else {
next()
}
})
To answer this in full would be kinda bulky so go and check out how I did that using meta here and implemented a beforeEach rule here

How to correct, Maximum call stack size exceeded, vue-router?

Good day.
I have the following error:
[vue-router] uncaught error during route navigation:
vue-router.esm.js:1897 RangeError: Maximum call stack size exceeded
at String.replace (<anonymous>)
at resolvePath (vue-router.esm.js:597)
at normalizeLocation (vue-router.esm.js:1297)
at Object.match (vue-router.esm.js:1341)
at VueRouter.match (vue-router.esm.js:2461)
at HashHistory.transitionTo (vue-router.esm.js:1865)
at HashHistory.push (vue-router.esm.js:2267)
at eval (vue-router.esm.js:1952)
at router.beforeEach (index.js:116)
at iterator (vue-router.esm.js:1935)
According to the error it is in my file of routes, which I have it in the following way:
import Vue from 'vue'
import Router from 'vue-router'
import Home from '../components/Home'
import Vehiculos from '../components/Vehiculos'
import Perfil from '../components/Perfil'
import Login from '../components/Login'
import TutorialIntroduccion from '../components/TutorialIntroduccion'
import Politicas from '../components/Politicas'
import Parqueo from '../components/Parqueo'
import Politicas from '../components/Politicas'
import Historial from '../components/Historial'
import firebase from 'firebase'
Vue.use(Router)
let tutorialVisto = localStorage.getItem("Tutorial");
const router = new Router({
routes: [
{
path: '*',
redirect: '/login'
},
{
path: '/',
redirect: '/tutorial'
},
{
path: '/tutorial',
name: 'tutorial',
component: TutorialIntroduccion,
meta: {
tutorialVisto: tutorialVisto,
autentificado: false
},
beforeEnter: (to, from, next) => {
let tutorialVisto = to.matched.some(record=>record.meta.tutorialVisto);
if (tutorialVisto)next('login');
else next();
}
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/home',
name: 'home',
component: Home,
meta: {
autentificado: true
}
},
{
path: '/parqueo/:id',
name: 'parqueo',
component: Parqueo,
meta: {
autentificado: true
}
},
{
path: '/vehiculos',
name: 'vehiculos',
component: Vehiculos,
meta: {
autentificado: true
}
},
{
path: '/perfil',
name: 'perfil',
component: Perfil,
meta: {
autentificado: true
}
},
{
path: '/politicas',
name: 'politicas',
component: Politicas,
meta: {
autentificado: true
},
},
{
path: '/historial',
name: 'historial',
component: Historial,
meta:{
autentificado: true
}
}
]
})
router.beforeEach((to, from, next) => {
let usuario = firebase.auth().currentUser; //Debe ser otra Promesa si esta autenticado o no.
let autorizacion = to.matched.some(record=>record.meta.autentificado);
let tutorialVisto = to.matched.some(record=>record.meta.tutorialVisto);
if (autorizacion && !usuario) {
next('login');
}
else if (!autorizacion && usuario) {
next('home');
}
else{
next();
}
})
export default router;
The problem arises when I am in the parking lot view and then when I log in, it does not redirect me to the vita login, but it gives me that error and stays in the same view, although it does close the firebase session. If I am in any of the other views, for example, vehicles, profile or main and then I give in closing session does not generate me error.
The session closing code is the following:
linkto(pathname) {
this.$router.push({ path: pathname })
if(pathname=="/login") {
firebase.auth().signOut().then(() => this.$router.replace('login'))
}
},
According to vue router's docs:
{
// will match everything
path: '*'
}
It is usually used to redirect to a 404 page not another route. In your case, you are calling a new route /login and it matches in * as well, which causes the loop and the Maximum call stack size exceeded.
You have multiple spelling mistakes in your next handlers. These calls accept the name of a route, and if you pass it a name of an route that does not exists, it will behave unspecified.
From what I can see is that you are trying to redirect to login, while you actually called the route Login
It happened to me too.
So after my investigation I have found if you are using Vuejs v. 2.* you have to use the vue-router of v. 2.* (not version 3).
Please see the following examaple:
package.json:
"dependencies": {
"vue": "^2.6.11",
"vue-router": "^2.8.1"
router.ts:
import Vue from "vue";
import VueRouter, { RouteConfig } from "vue-router";
import { Component } from "vue-router/types/router";
import Home from "#/views/Home.vue";
import NotFound from "#/views/NotFound.vue";
import MyPage from "#/views/MyPage.vue";
Vue.use(VueRouter);
const routes: Array<RouteConfig> = [
{
path: "/",
name: "Home",
component: Home as Component
},
{
path: "/my-page",
name: "MyPage",
component: MyPage as Component
},
{
// will match everything else
path: '*',
name: "NotFound",
component: NotFound as Component
}
];
const router = new VueRouter({
mode: "history",
routes
});
export default router;