vuejs / vue-router

🚦 The official router for Vue 2
http://v3.router.vuejs.org/
MIT License
18.99k stars 5.06k forks source link

useRouter() return undefined #3379

Closed zyyv closed 3 years ago

zyyv commented 3 years ago

when i use useRouter break away vue hooks , it return undefined, but in the vue hooks, it return Router. so i dont know this reason, Please answer me how to use useRouter is normal js, because i want to use it in vuex image

vue-bot commented 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!

timsayshey commented 3 years ago

Welp, this is what comes up on Google when you search the issue.

@chris-zhu Did you figure this out?

cecitorres commented 3 years ago

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!

CJLees01 commented 3 years ago

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:

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.

PeppeL-G commented 2 years ago

That composables should only be used in Vue components (in setup() is now documented here:

https://vuejs.org/guide/reusability/composables.html#usage-restrictions

ffxsam commented 1 year ago

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);
PeppeL-G commented 1 year ago

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.

ffxsam commented 1 year ago

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>.

DmitiryKd2 commented 1 year ago

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>
Sovai commented 1 year ago

If I only able to use it in setup script, how do I redirect route in axios interceptor file then?

gaminglifex commented 1 year ago

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?

alexwenzel commented 1 year ago

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: {...}})
})
fullmooooon commented 11 months ago

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>
haobarry commented 11 months ago

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

fullmooooon commented 11 months ago

@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.

sombriks commented 10 months ago

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()}`)
  }
}
alex-dow commented 2 months ago

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

cwdx commented 1 month ago

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.

Stalinko commented 3 weeks ago

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.