-
Notifications
You must be signed in to change notification settings - Fork 506
Description
Versions:
@inertiajs/vue3
version: 2.0.11
(I believe the issue is in the code common to all client types)
Describe the problem:
I am in the middle of porting a sizeable webapp from Adonisjs v4/Vuejs v2 to Adonisjs v6/Vuejs v3.
A lot has changed on both the server and client side, including the introduction of Inertia as the glue in the framework. But the most pertinent point to this issue is I am still using the original vue-router router and not Inertia's own router.
My app client's routing code also uses the window.history object to restore state after a full page refresh, which worked fine before I migrated my code to the new framework version. But now that Inertia is in the mix, the saved window.history state is being overwritten after the very end of the F5 page refresh. The result is that the refresh works once, but if you hit F5 again, the route state is already gone and the webapp functionality breaks.
I eventually tracked the culprit down to Inertia's history management code in packages/core/src/history.ts (doReplaceState:175) where you replace the existing history state with component and scroll state data. The fact that you delay the state replacement until the next event loop tick (replaceState:156) made it all the more difficult to figure out what was going on, since from the app's point of view, the page refresh completes fine with the routing state intact.
I understand that you need to use the window.history object to implement the ability to save component state between refreshes in your homegrown router, and I fully understand that I might be an edge case where I am continuing to use vue-router for legacy reasons, but given you delay overwriting the app-saved history state until after all processing within the app is complete, it makes it very difficult to workaround with 100% certainty.
At the moment, I have to save my history state in the onMounted() hook of my Main component and then replace your replacement within a setTimeout(100) call. Not very satisfactory.
So, what I am asking is can you fix in the Inertia code so it does not wipe out the existing history when adding in the state you require?
Perhaps you could check for a specific property in the existing history state, like "params" and make sure that's carried over if present? That seems like a very safe fix to me, since it doesn't change the timing of the call. Maybe you can have a global setting that allows developers to specify which history.state properties to preserve and carry forward?
While my case might be an edge case, I can certainly see that other developers who are using window.history for other reasons could be tripped up by this issue. It's not easy to debug changes to the window.history object find out where they're coming from, especially when they're made outside the scope of the app's execution.
It seems like app frameworks like Inertia should be conscious of the possibility that apps may want to use the history.state global for their own purposes and find a way to share its use and accommodate their code.
Steps to reproduce:
Put the following code in the main page component setup on the Vue 3 client:
onMounted(() => {
// Any arbitrary app state
history.replaceState({title: 'My App', message: 'Welcome to My App'}, '')
// App state is saved in history.state as expected
console.log('Post Refresh History', history.state)
setTimeout(() => {
// 100ms later, the app state has been overwritten by Inertia.
console.log('Delayed Post Refresh History', history.state)
}, 100)
})
Current workaround:
Add code in the main page component setup on the Vue 3 client:
onMounted(() => {
// Clone the desired state information
const state = structuredClone(history.state)
setTimeout(() => {
// Restore the desired state information
history.replaceState(state, '')
}, 100) // <-- timing could be a problem in some cases
})