Closed zyyv closed 3 years ago
Hello, thank you for taking time filling this issue!
However, we kindly ask you to use our Issue Helper when creating new issues, in order to ensure every issue provides the necessary information for us to investigate. This explains why your issue has been automatically closed by me (your robot friend!).
I hope to see your helper-created issue very soon!
Welp, this is what comes up on Google when you search the issue.
@chris-zhu Did you figure this out?
useRouter must be called inside of setup() (documentation link)
But you can use composition api for extract logic of routing
<script>
import { useRouter } from "vue-router"
const useRouterCustom = () => {
const router = useRouter();
const goToRoot = () => {
router.push('/')
}
return {
goToRoot
}
}
export default {
name: 'App',
setup() {
const { goToRoot } = useRouterCustom();
}
}
</script>
I hope it helps, good luck!
I think @chris-zhu was originally aiming to access the router to perform actions such as pushing new URLs inside of Vuex.
Do I understand from your explanation @cecitorres that this is impossible under Vuex4? That the only place where the router can really be reached is within the setUp()
function of Vue components, which in turn means that anybody still using the Options API is out of luck and needs to upgrade?
If this is true, then it seems that the "answer" is a pretty lengthy bit of re-work to:
this.$router
under Vuex 3, and change them to accept a new router
argument.useRouter()
in the setup()
method of all of those Vue Components, and pass it into Vuex using the new parameter.I completely agree that Composition API is the way forward, but it feels like this forcing an upgrade which I was hoping to popstpone until later.
Or is there any easier way?
[EDIT]
While the above set of steps should probably work, on reflection I've realised that having routing code in Vuex is a red flag from a separation of concerns perspective; Vuex should be about storing re-usable data and not handling routing.
So my approach will be to move that logic out into normal Vue Components, where the original this.$router
syntax will still work, which will allow me to continue using Options API short-term if I like.
That composables should only be used in Vue components (in setup()
is now documented here:
https://vuejs.org/guide/reusability/composables.html#usage-restrictions
What if I have a composable that needs to reference another composable? E.g. my composable needs to use useRouter
to grab the route? I suppose I have to use dependency injection?
<script setup lang="ts">
import { useRouter } from 'vue-router';
import { useLogin } from '../composables/login';
const router = useRouter();
const { attemptLogin } = useLogin(router);
You can use a composable inside other composables, so you can use useRouter()
in the implementation of useLogin()
.
My phrasing before might be a bit misleading. A more accurate phrasing (if I have understood it correct) might be composables can only be used when the code in setup()
runs.
useRouter()
was returning undefined for me in my own composable, though. I'll have to double-check the stack again to make sure it's originating from a <script setup>
.
Add useRouter change onMounted to async add await router.isReady() before setting parameters
<script lang="ts" setup>
import {onMounted, ref, Ref} from 'vue'
import {useRoute, useRouter} from 'vue-router'
const route = useRoute()
const router = useRouter()
let companyId: Ref<string | string[]> = ref([])
onMounted(async () => {
console.log('Before Router Preparation', route.name);
await router.isReady();
console.log('After Router Preparation', route.name);
companyId.value = route.params.id
})
</script>
If I only able to use it in setup script, how do I redirect route in axios interceptor file then?
useRouter()
was returning undefined for me in my own composable, though. I'll have to double-check the stack again to make sure it's originating from a<script setup>
.
Sorry. Do you have any solutions on that?
I have the same problem and i only could solve it, when calling useRouter()
withing a hook. Outside on hook it does not work.
/**
* Lifecycle.
*/
onBeforeMount(() => {
const redirectUrl = useRouter().resolve({name: 'blabla', params: {...}})
})
This is not the first time I have faced this problem, I think I have finally grown up to have the ability to solve it. The correct solution is to cache the call results during setup.
<template>
<div @click="clickHandle">
</div>
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router';
let useRouterResult = useRouter();
let clickHandle = ()=>useRouterResult.go(-1)
</script>
You can try using the following code for inspiration
<script setup lang="ts">
import { useRouter } from 'vue-router';
console.log(useRouter(), 1);
setTimeout(() => console.log(useRouter(), 2), 3000);
let useRouterResult = useRouter();
setTimeout(() => console.log(useRouterResult, 3), 5000);
</script>
This behavior, which can only be called during setup initialization, is like a joke that will severely hit beginners. To be honest I think it's best for the document to provide a detailed explanation of this situation, rather than being confusing https://router.vuejs.org/api/#useRoute:~:text=RouteLocationNormalizedLoaded-,useRouter,Router,-FREE%20WEEKEND
Examples of mistakes that many people make
<template>
<div @click="clickHandle">
</div>
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router';
let clickHandle = ()=>useRouter().go(-1)
</script>
you can import you created router to use in any files,
For example:
// src/router/index.ts
exprot const router = createRouter({
...
})
// orther files
import { router } from '@/router/index'
or use window.location.href
@haobarry Thank you for your answer. I think I have a better understanding of 'vue router' and the situation I have encountered.
I am using the quasar framework, and its src/router/index.ts
does not default to exporting Router
.
I just realized It tells me in the annotation that this can be done.
But I am a non native English speaker, and I often ignore annotations and have been unwilling to modify the default code in the past.
just got hit by this issue, sometimes useRouter()
returned undefined.
Another option is to use a dynamic import:
// api.js
const req = async ({ method = 'POST', uri, payload }) => {
const token = useUserStore().store.token
const { router } = await import('./router') // export const router in a file called router.js near api.js
const url = `${import.meta.env.VITE_API_URL}${uri}`
const headers = {
'Content-Type': 'application/json',
Accept: 'application/json'
}
if (token) headers['Authorization'] = `Bearer ${token}`
try {
const result = await fetch(url, {
body: JSON.stringify(payload),
headers,
method
})
if(result.status < 400)
return result.json()
else {
throw result
}
} catch (e) {
if(e.status === 401) {
router.push('/auth')
}
throw new Error(`${e.status} - ${await e.text()}`)
}
}
This issue is the top result on google for useRouter() undefined.
So perhaps this is a good place to explain how I fixed it. My issue was that there were duplicate vue-routers.
The project uses yarn v1 workspaces. One of my packages depended on vue-router v4.4.1 and another one of my packages depended on vue-router v4.4.2. A vue component in package one called useRouter() and it returned undefined. This only happened in production code, not while running the vite dev server.
After resolving this discrepancy and ensuring everyone was using the same version of vue-router, everything worked as expected.
Hope this helps
This issue is the top result on google for useRouter() undefined.
So perhaps this is a good place to explain how I fixed it. My issue was that there were duplicate vue-routers.
The project uses yarn v1 workspaces. One of my packages depended on vue-router v4.4.1 and another one of my packages depended on vue-router v4.4.2. A vue component in package one called useRouter() and it returned undefined. This only happened in production code, not while running the vite dev server.
After resolving this discrepancy and ensuring everyone was using the same version of vue-router, everything worked as expected.
Hope this helps
Thank you, that helped me. We're using Yarn workspaces with NX.
Solved it by creating a new composable useRouterShared.ts
:
import { useRouter } from 'vue-router';
let router: ReturnType<typeof useRouter>;
export default () => {
if (!router) {
router = useRouter();
}
return router;
};
Then you just replace useRouter
with useRouterShared
.
Important - first call of useRouterShared()
must be made inside setup
context.
when i use
useRouter
break away vue hooks , it returnundefined
, but in the vue hooks, it returnRouter
. so i dont know this reason, Please answer me how to useuseRouter
is normal js, because i want to use it in vuex