I am building a single page application using vanilla typescript and golang. I am having trouble managing my history properly.
To be clear, here is how the application functions on initial page load.
- You make a request to the go http server.
- You get back a blank HTML template with a script tag at the bottom.
- The browser makes a request to the server for the .js bundle.
- Javascript bundle is retrieved and then takes the browser’s window.location.pathname and runs an associated handler function
- The handler executes middleware.
- The handler renders the view.
- Request complete.
From here, the client-side javascript handles routing through different views via a redirect function.
Fun, now we get into some code.
Here is the global session I use to track the users history and potential state values. It also contains the apps router.
export type Session = {
Router: Map<string, () => void>,
History: string[],
State: Map<string, any>,
}
export let session = {
Router: new Map<string, () => void>(),
History: new Array<string>(),
State: new Map<string, any>(),
}
Routes are simply registered in the apps root-level index.ts
import { enableHistory, preventForwardNavigation } from "./client/events"
import { session } from "./client/global"
import { handlerHome, handlerLogin } from "./client/handlers"
enableHistory()
preventForwardNavigation()
session.Router.set("/", handlerHome)
session.Router.set("/login", handlerLogin)
let handler = session.Router.get(window.location.pathname)
if (handler) {
handler()
} else {
console.log("404")
}
Let’s take a closer look at the enableHistory() and preventForwardNavigation() functions.
export const enableHistory = () => {
window.addEventListener('popstate', (e: PopStateEvent) => {
let previousURL = session.History[session.History.length - 2]
session.History = session.History.slice(0, session.History.length - 2)
if (previousURL) {
let handler = session.Router.get(previousURL)
if (handler) {
window.history.pushState({}, "", previousURL)
handler()
}
} else {
history.back()
}
})
}
export const preventForwardNavigation = () => {
if (window.history && window.history.forward) {
window.history.forward = function() {
alert("Forward navigation is disabled.");
};
}
}
Okay, so as an overview, the enableHistory() grabs the next to last element off the session.History stack (which represents the previous path loaded) and then calls the appropriate handler function found in session.Router.
Lets take a look at a handler and then a view so we can see what it looks like when history is pushed onto session.History
Here is a handler which is called when a user hits “/”:
export const handlerHome = () => {
document.title = "CFA Suite - Home"
mwRunner([mwInit, () => {
render(ViewHome())
}, mwTele, mwLinks, mwLog])
}
The handler calls middleware and then makes a call to ViewHome() which ultimately generates the HTML the user sees and interacts with.
Okay, now we are closing in on the problem. Lets take a look at mwLinks, which is a middleware which modifies the way links function in the application.
export const mwLinks = (): void => {
let links = document.querySelectorAll('a')
links.forEach(link => {
link.addEventListener('click', (e: Event) => {
e.preventDefault()
let href = link.getAttribute('href')
if (href) {
redirect(href)
}
})
})
}
As you can see, all links in the application make a call to redirect() when they are clicked. Here the function redirect:
export function redirect(fullPath: string) {
window.history.pushState({}, "", fullPath)
let handler = session.Router.get(getBasePath(fullPath))
if (handler) {
handler()
} else {
console.log("404")
}
}
Okay, so as you can see, history is really managed 3 places in the application. On initial page load, we make calls to enableHistory() and preventForwardNavigation() and then when a link is clicked, we make called to redirect() which pushes state to window.history and pushes a path on session.History.
So to be clear, history in the app works. But I have two problems:
-
The forward button does not work properly. That is why I disabled it. And I am not 100% sure why it doesn’t work properly.
-
When I right click my back button (like the actual back button in the browser) I notice my history stack continues to grow after clicking the back button.
How can I resolve this problem and make my forward button work properly while having an accurate history stack within the actual browser history stack (not session.History).
I have tried extensively to solve this problem and has been the hardest issue in my entire project.
Thank you so much for your time, I appreciate you!