Use history.pushState() for internal links.
rodzic
b28a0114b9
commit
a13f989ab3
|
@ -1,4 +1,4 @@
|
||||||
import React from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import { PageContext, PAGE_QUERY_ARG } from "./page";
|
import { PageContext, PAGE_QUERY_ARG } from "./page";
|
||||||
import { pageNames, Pages, toPageName, DEFAULT_PAGE } from "./pages";
|
import { pageNames, Pages, toPageName, DEFAULT_PAGE } from "./pages";
|
||||||
|
@ -11,13 +11,41 @@ if (!appEl) {
|
||||||
throw new Error(`Unable to find #${APP_ID}!`);
|
throw new Error(`Unable to find #${APP_ID}!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getWindowSearch(): URLSearchParams {
|
||||||
|
return new URLSearchParams(window.location.search);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call the given handler whenever a `popstate` event
|
||||||
|
* occurs.
|
||||||
|
*
|
||||||
|
* Return a function that wraps `window.history.pushState()`;
|
||||||
|
* the given handler will be called immediately afterwards.
|
||||||
|
*/
|
||||||
|
function usePushState(onPushOrPopState: () => void) {
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener("popstate", onPushOrPopState);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("popstate", onPushOrPopState);
|
||||||
|
};
|
||||||
|
}, [onPushOrPopState]);
|
||||||
|
|
||||||
|
return function pushState(href: string) {
|
||||||
|
window.history.pushState(null, "", href);
|
||||||
|
onPushOrPopState();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const App: React.FC<{}> = (props) => {
|
const App: React.FC<{}> = (props) => {
|
||||||
const page = new URLSearchParams(window.location.search);
|
const [search, setSearch] = useState(getWindowSearch());
|
||||||
const currPage = toPageName(page.get(PAGE_QUERY_ARG) || "", DEFAULT_PAGE);
|
const updateSearchFromWindow = () => setSearch(getWindowSearch());
|
||||||
|
const currPage = toPageName(search.get(PAGE_QUERY_ARG) || "", DEFAULT_PAGE);
|
||||||
const PageComponent = Pages[currPage];
|
const PageComponent = Pages[currPage];
|
||||||
|
const pushState = usePushState(updateSearchFromWindow);
|
||||||
const ctx: PageContext = {
|
const ctx: PageContext = {
|
||||||
currPage,
|
currPage,
|
||||||
allPages: pageNames,
|
allPages: pageNames,
|
||||||
|
pushState,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
29
lib/page.tsx
29
lib/page.tsx
|
@ -1,21 +1,42 @@
|
||||||
import React, { useContext } from "react";
|
import React, { MouseEvent, useContext } from "react";
|
||||||
import type { PageName } from "./pages";
|
import type { PageName } from "./pages";
|
||||||
|
|
||||||
export type PageContext = {
|
export type PageContext = {
|
||||||
currPage: PageName;
|
currPage: PageName;
|
||||||
allPages: PageName[];
|
allPages: PageName[];
|
||||||
|
pushState: (href: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PageContext = React.createContext<PageContext>({
|
export const PageContext = React.createContext<PageContext>({
|
||||||
currPage: "vocabulary",
|
currPage: "vocabulary",
|
||||||
allPages: [],
|
allPages: [],
|
||||||
|
pushState: () => {
|
||||||
|
throw new Error("No page context is defined!");
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const PAGE_QUERY_ARG = "p";
|
export const PAGE_QUERY_ARG = "p";
|
||||||
|
|
||||||
const PageLink: React.FC<{ page: PageName }> = ({ page }) => (
|
function isNormalLinkClick(e: MouseEvent): boolean {
|
||||||
<a href={`?${PAGE_QUERY_ARG}=${encodeURIComponent(page)}`}>{page}</a>
|
return !e.shiftKey && !e.altKey && !e.metaKey && !e.ctrlKey && e.button === 0;
|
||||||
);
|
}
|
||||||
|
|
||||||
|
const PageLink: React.FC<{ page: PageName }> = ({ page }) => {
|
||||||
|
const href = `?${PAGE_QUERY_ARG}=${encodeURIComponent(page)}`;
|
||||||
|
const { pushState } = useContext(PageContext);
|
||||||
|
const handleClick = (e: MouseEvent) => {
|
||||||
|
if (isNormalLinkClick(e)) {
|
||||||
|
pushState(href);
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<a href={href} onClick={handleClick}>
|
||||||
|
{page}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const Navbar: React.FC<{}> = (props) => {
|
const Navbar: React.FC<{}> = (props) => {
|
||||||
const pc = useContext(PageContext);
|
const pc = useContext(PageContext);
|
||||||
|
|
Ładowanie…
Reference in New Issue