Vue3.3.2 - Is it possible to access useRoute within a composable function?
Asked Answered
L

1

5

I'm trying to access useRoute from within a composable function. Starting with vue 3.3.2 & vue-route 4.2.0, I set up a test using npm init vue@latest with the router enabled. No problem accessing useRoute via the composition API within the 'script setup' tag:

App.vue

<script setup>
import { RouterLink, RouterView } from 'vue-router'

import { useRoute } from 'vue-router'
const route = useRoute()

import { computed } from 'vue'
const path = computed(() => {
  console.log(`step 1`)
  if (!route) {
    console.log(`step 2`)
    return
  }
  if (!route.path) {
    console.log(`step 3`)
    return
  }
  console.log(`step 4`)
  return route.path
})
</script>

<template>
  <p>path = {{ path }}</p>
  <header>
    <nav>
      <RouterLink to="/">Home</RouterLink>
      <RouterLink to="/about">About</RouterLink>
    </nav>
  </header>

  <RouterView />
</template>

In the above code, the computed function is re-calculated each times the route changes, reaching step 1 then step 4 (success).

But when I move this code to a composable it breaks:

App.vue

<script setup>
import { RouterLink, RouterView } from 'vue-router'
import path from '@/composables/useRouteTest'
</script>

composables/useRouteTest.js

import { useRoute } from 'vue-router'
const route = useRoute()

import { computed } from 'vue'
const path = computed(() => {
  console.log(`step 1`)
  if (!route) {
    console.log(`step 2`)
    return
  }
  if (!route.path) {
    console.log(`step 3`)
    return
  }
  console.log(`step 4`)
  return route.path
})

export default {
  path
}

Within the composable, the computed function never progresses beyond step 2, and is only called once, even when I navigate to different routes.

Does useRoute not work within composables?

I've spend many hours trying the following alternatives: different Vue versions; using watch instead of computed; 'useRouter' rather than 'useRoute'; using watch with async invocation (https://router.vuejs.org/guide/advanced/composition-api.html). Always the same result.

Lilith answered 15/5, 2023 at 4:17 Comment(0)
A
6

useRoute is only available for use inside script setup. What you can do in the composable is wrap its usage in a function then import and call the function from your component. This is the recommended way to make most composables so they can share the context of the component they're called from.

composables/useRouteTest.js

import { useRoute } from 'vue-router'
import { computed } from 'vue'

export function useRouteTest() {
  const route = useRoute()

  const path = computed(() => {
    console.log(`step 1`)
    if (!route) {
      console.log(`step 2`)
      return
    }
    if (!route.path) {
      console.log(`step 3`)
      return
    }
    return route.path
  })

  return {
    path
  }
}

App.vue

<script setup>
import { useRouteTest } from '@/composables/useRouteTest'

const { path } = useRouteTest()
</script>
Absorb answered 15/5, 2023 at 4:40 Comment(4)
Thanks yoduh, works perfectly and your explanation clears up my confusion. I somehow missed this point in the documentation.Lilith
@Lilith if this answers your question, please consider accepting this as the solution.Jurist
Since useRoute() is a composable and not a utility, I don't understand why it should not be used in another composable / outside the setup function. Where did you find that information @yoduh? There is one catch though – useRoute() is not reactive. That caused problems for me. You can however access useRouter().currentRoute which returns the same information but as a ref.Ebenezer
Vue's restrictions on composables explains why they should only be used inside setup functions. Also, the object returned by useRoute() is reactive, from the docs: "The route object is a reactive object"... To explain why the useRoute object is not reactive in your case would require exploring the circumstances of that unique case (feel free to make a new question about it).Absorb

© 2022 - 2024 — McMap. All rights reserved.