Extract the navigation link to a useNavigationLink hook

This commit is contained in:
Quentin Gliech
2023-09-14 11:26:01 +02:00
parent 5f283573e0
commit 17519846f3
3 changed files with 62 additions and 35 deletions
+5 -35
View File
@@ -12,18 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { useAtomValue, useSetAtom } from "jotai";
import { useTransition } from "react";
import styles from "./Link.module.css";
import { appConfigAtom, routeAtom } from "./atoms";
import { Route, routeToPath } from "./routes";
// Filter out clicks with modifiers or that have been prevented
const shouldHandleClick = (e: React.MouseEvent): boolean =>
!e.defaultPrevented &&
e.button === 0 &&
!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
import { Route } from "./routes";
import { useNavigationLink } from "./useNavigationLink";
const Link: React.FC<
{
@@ -32,13 +23,7 @@ const Link: React.FC<
kind?: "button";
} & React.HTMLProps<HTMLAnchorElement>
> = ({ route, children, kind, className, ...props }) => {
const config = useAtomValue(appConfigAtom);
const path = routeToPath(route);
const fullUrl = config.root + path;
const setRoute = useSetAtom(routeAtom);
// TODO: we should probably have more user control over this
const [isPending, startTransition] = useTransition();
const { onClick, href, pending } = useNavigationLink(route);
const classNames = [
kind === "button" ? styles.linkButton : "",
@@ -46,23 +31,8 @@ const Link: React.FC<
].join("");
return (
<a
href={fullUrl}
onClick={(e: React.MouseEvent): void => {
// Only handle left clicks without modifiers
if (!shouldHandleClick(e)) {
return;
}
e.preventDefault();
startTransition(() => {
setRoute(route);
});
}}
className={classNames}
{...props}
>
{isPending ? "Loading..." : children}
<a href={href} onClick={onClick} className={classNames} {...props}>
{pending ? "Loading..." : children}
</a>
);
};
+1
View File
@@ -18,3 +18,4 @@ export type { Route, Location } from "./routes";
export { pathToRoute, routeToPath } from "./routes";
export { getRouteActionRedirection } from "./actions";
export { routeAtom, locationAtom, appConfigAtom } from "./atoms";
export { useNavigationLink } from "./useNavigationLink";
+56
View File
@@ -0,0 +1,56 @@
// Copyright 2023 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { useAtomValue, useSetAtom } from "jotai";
import { useTransition } from "react";
import { appConfigAtom, routeAtom } from "./atoms";
import { Route, routeToPath } from "./routes";
// Filter out clicks with modifiers or that have been prevented
const shouldHandleClick = (e: React.MouseEvent): boolean =>
!e.defaultPrevented &&
e.button === 0 &&
!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
/**
* A hook which controls a navigation link to a given route
*/
export const useNavigationLink = (
route: Route,
): {
onClick: (event: React.MouseEvent) => void;
href: string;
pending: boolean;
} => {
const config = useAtomValue(appConfigAtom);
const path = routeToPath(route);
const href = config.root + path;
const setRoute = useSetAtom(routeAtom);
const [pending, startTransition] = useTransition();
const onClick = (e: React.MouseEvent): void => {
// Only handle left clicks without modifiers
if (!shouldHandleClick(e)) {
return;
}
e.preventDefault();
startTransition(() => {
setRoute(route);
});
};
return { onClick, href, pending };
};