Created
January 29, 2025 06:18
-
-
Save sor4chi/b7b6135f1c86459374420763a9479763 to your computer and use it in GitHub Desktop.
Hono SPA Router
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
MouseEventHandler, | |
ReactNode, | |
StrictMode, | |
useCallback, | |
useSyncExternalStore, | |
} from "react"; | |
import { createRoot } from "react-dom/client"; | |
import { Hono } from "hono"; | |
import { Action, createBrowserHistory, Update } from "history"; | |
let page: ReactNode = undefined; | |
const history = createBrowserHistory(); | |
const ROUTER_UPDATE_EVENT = "hono-spa-router:update"; | |
const app = new Hono(); | |
app.get("/", (c) => c.text("Hello, Hono SPA Router!")); | |
app.get("/a", (c) => c.text("Hello, A!")); | |
app.get("/b", (c) => c.text("Hello, B!")); | |
async function onLocationChange({ location }: Update): Promise<void> { | |
const res = await app.request(location.pathname); | |
page = await res.text(); | |
window.dispatchEvent(new CustomEvent(ROUTER_UPDATE_EVENT)); | |
} | |
history.listen(onLocationChange); | |
await onLocationChange({ | |
action: Action.Replace, | |
location: history.location, | |
}); | |
function Link(props: { to: string; children: any; onClick?: Function }) { | |
const { to, children, onClick, ...others } = props; | |
const handleClick = useCallback( | |
({ onClick, to }: { onClick?: Function; to: string }): MouseEventHandler => | |
(e) => { | |
onClick?.(e); | |
e.preventDefault(); | |
history.push(to); | |
}, | |
[onClick, to] | |
); | |
return ( | |
<a href={to} {...others} onClick={handleClick({ onClick, to })}> | |
{children} | |
</a> | |
); | |
} | |
function getSnapshot() { | |
return page; | |
} | |
function subscribe(callback: () => void) { | |
window.addEventListener(ROUTER_UPDATE_EVENT, callback); | |
return () => window.removeEventListener(ROUTER_UPDATE_EVENT, callback); | |
} | |
function App() { | |
const page = useSyncExternalStore(subscribe, getSnapshot); | |
return <main>{page}</main>; | |
} | |
createRoot(document.getElementById("root")!).render( | |
<StrictMode> | |
<h1>Hono SPA Router</h1> | |
<nav> | |
<ul> | |
<li> | |
<Link to="/">Home</Link> | |
</li> | |
<li> | |
<Link to="/a">A</Link> | |
</li> | |
<li> | |
<Link to="/b">B</Link> | |
</li> | |
</ul> | |
</nav> | |
<App /> | |
</StrictMode> | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment