router icon indicating copy to clipboard operation
router copied to clipboard

Allow accessing history state from Route-objects

Open amxmln opened this issue 1 year ago • 6 comments

While I was refactoring my code to remove artificial params in lieu of the 4.1.4 release to write my data to the history.state object, I noticed that I can pass a state property to $router.push() and was kind of surprised that I could not retrieve it with $route.state (like I can with queries or params) but had to access it using window.history.state.

Would it be possible to implement a state property containing only the passed state in the RouteLocationNormalizedLoaded interface?

That would make it a more intuitive replacement for the removed artificial params IMO.

amxmln avatar Aug 26 '22 14:08 amxmln

history.state has a few limitations and I'm still not sure it can be included in all locations. For example, it cannot be included in to locations in navigation guards, so, unfortunately, it cannot be added to any existing RouteLocation.... This would need yet another route location type variant.

Note that right now this can be achieved with a guard:

router.afterEach(to => {
  to.state = history.state
})

but there are other alternatives like a composable useHistoryState():

import { shallowRef, onScopeDispose } from 'vue'

function useHistoryState<T = unknown>(router?: Router) {
  const state = shallowRef<T>(history.state)
  const remove = (router || useRouter()).afterEach(() => {
    state.value = history.state
  })
  onScopeDispose(remove)
  return state
}

This variant is in a pending PR at #2106

I think ultimately, we want to go with another route location variant but it would be helpful to explore other possibilities and possible caveats.

posva avatar Aug 26 '22 16:08 posva

Hey! I found this issue looking for an alternative solution for sending arbitrary params (or artificial params @amxmln) when using router.push since this is not supported anymore with regular route params. Since you can pass state as an option in the router.push I thought that could be the perfect candidate but I'm hitting issues as well when trying to retrieve the state I'm sending. My use-case is that I want to have different scroll behaviour depending on where the navigation push was initiated (we don't want to scroll at the top when switching tabs)

{
scrollBehavior (to) {
    // It would be great to be able to retrieve history state or more generally artificial params here
}, 
}

Bottom line, what I'm trying to say is that it seems there is plenty of use-case for artificial params (aka navigation-based arbitrary data) and I understand history state is probably not the right fit. But with the new version preventing arbitrary params what are the alternatives ?

AlexandreBonaventure avatar Aug 31 '22 14:08 AlexandreBonaventure

@AlexandreBonaventure alternatives are in the changelog

posva avatar Aug 31 '22 15:08 posva

Yes, I appreciate you are providing migration steps, but going back to my specific use-case I don't see any alternatives working for me:

  • Putting the data in a store: does not feel right because that's really not a global state
  • Move the data to an actual param: not relevant either because that's not a URL-based state
  • Pass the data as state to save it to the History API state: as per this conversation is not fitting the use-case
  • Pass it as a new property to to.meta during navigation guards: does not seem to work for me because I can't set the meta when pushing the navigation like that for instance router.push({ name: 'test', meta: { test: 'heyyy' } });

Let me know if I missed something or if I'm off-topic.

I know this is a github issue and not a support forum, but I wonder if the above scenario is warranting another API for dealing with so-called artificial params, which could benefit to other users as well.

--- Alternatives (things that could work for me)

  • allow to send meta with router.push/replace/etc (I like the simplicity of this one, but I have to say being a user for a long time of vue-router, that modifying meta object feels like an undocumented/hidden feature, since to me meta object is declarative)
  • provide a brand new option in RouteLocationOptions
  • or ??

AlexandreBonaventure avatar Aug 31 '22 16:08 AlexandreBonaventure

There won’t be a new api for artificial params (aka state) as it was removed before for being a bad practice (for the reasons mentioned in the changelog and others I probably forgot since it’s been more than 5 years 😅)

Your use case does sound like it should use a store. Stores are not only for global state or you could also say that if state lives through different pages, it is global. Or maybe https://github.com/vuejs/rfcs/discussions/460 👀

posva avatar Aug 31 '22 19:08 posva

@AlexandreBonaventure what about using query instead of params

mirko77 avatar Jun 15 '23 10:06 mirko77