Skip to content

Commit

Permalink
chore(nextjs13): upgrade link usage
Browse files Browse the repository at this point in the history
  • Loading branch information
keyserj committed May 30, 2023
1 parent 5fd1db6 commit bf6ac1c
Show file tree
Hide file tree
Showing 3 changed files with 15 additions and 113 deletions.
2 changes: 1 addition & 1 deletion web/src/common/components/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { NextPage } from "next";
import Image from "next/image";
import { ReactNode } from "react";

import Link from "./Link";
import { Link } from "./Link";

interface NavLinkProps {
href: string;
Expand Down
118 changes: 10 additions & 108 deletions web/src/common/components/Link.tsx
Original file line number Diff line number Diff line change
@@ -1,113 +1,15 @@
// Combine Material Link with Nextjs Link
// Stolen from https://github.com/mui/material-ui/blob/a7dbc3c61013941cb331ae01fffb9620088b6cfa/examples/nextjs-with-typescript/src/Link.tsx
// Combine look of Material Link with functionality of Nextjs Link.

import MuiLink, { type LinkProps as MuiLinkProps } from "@mui/material/Link";
import { styled } from "@mui/material/styles";
import clsx from "clsx";
import NextLink, { LinkProps as NextLinkProps } from "next/link";
import { useRouter } from "next/router";
import * as React from "react";

// Add support for the sx prop for consistency with the other branches.
const Anchor = styled("a")({});

interface NextLinkComposedProps
extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, "href">,
Omit<NextLinkProps, "href" | "as" | "onClick" | "onMouseEnter" | "onTouchStart"> {
to: NextLinkProps["href"];
linkAs?: NextLinkProps["as"];
}

export const NextLinkComposed = React.forwardRef<HTMLAnchorElement, NextLinkComposedProps>(
function NextLinkComposed(props, ref) {
const { to, linkAs, replace, scroll, shallow, prefetch, locale, ...other } = props;

return (
<NextLink
href={to}
prefetch={prefetch}
as={linkAs}
replace={replace}
scroll={scroll}
shallow={shallow}
passHref
locale={locale}
>
<Anchor ref={ref} {...other} />
</NextLink>
);
}
);

export type LinkProps = {
activeClassName?: string;
as?: NextLinkProps["as"];
href: NextLinkProps["href"];
linkAs?: NextLinkProps["as"]; // Useful when the as prop is shallow by styled().
noLinkStyle?: boolean;
} & Omit<NextLinkComposedProps, "to" | "linkAs" | "href"> &
Omit<MuiLinkProps, "href">;

// A styled version of the Next.js Link component:
// https://nextjs.org/docs/api-reference/next/link
const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(function Link(props, ref) {
const {
activeClassName = "active",
as,
className: classNameProps,
href,
linkAs: linkAsProp,
locale,
noLinkStyle,
prefetch,
replace,
// role, // Link don't have roles.
scroll,
shallow,
...other
} = props;
// Tried setting NextLink as the default component of MuiLink via MUI themeing (https://stackoverflow.com/a/74419666),
// but it would hit an infinite recursion issue without making a custom component anyways.

const router = useRouter();
const pathname = typeof href === "string" ? href : href.pathname;
const className = clsx(classNameProps, {
[activeClassName]: router.pathname === pathname && activeClassName,
});
// TODO #167: for some reason, upgrading to Next 13 caused MuiLink to throw a server-client class
// mismatch error if any decoration props are set.

const isExternal =
typeof href === "string" && (href.startsWith("http") || href.startsWith("mailto:"));

if (isExternal) {
if (noLinkStyle) {
return <Anchor className={className} href={href} ref={ref} {...other} />;
}

return <MuiLink className={className} href={href} ref={ref} {...other} />;
}

const linkAs = linkAsProp ?? as;
const nextjsProps = {
to: href,
linkAs,
replace,
scroll,
shallow,
prefetch,
locale,
};

if (noLinkStyle) {
return <NextLinkComposed className={className} ref={ref} {...nextjsProps} {...other} />;
}
import MuiLink, { type LinkProps as MuiLinkProps } from "@mui/material/Link";
import NextLink from "next/link";
import { forwardRef } from "react";

return (
<MuiLink
component={NextLinkComposed}
className={className}
ref={ref}
{...nextjsProps}
{...other}
/>
);
export const Link = forwardRef<HTMLAnchorElement, MuiLinkProps>(function Link(props, ref) {
return <MuiLink component={NextLink} ref={ref} {...props} />;
});

export default Link;
8 changes: 4 additions & 4 deletions web/src/pages/index.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ArrowDownward } from "@mui/icons-material";
import { Box, Button, Divider, Typography } from "@mui/material";
import type { NextPage } from "next";
import Head from "next/head";
import Link from "next/link";
import NextLink from "next/link";

import { YoutubeEmbed } from "../common/components/YoutubeEmbed/YoutubeEmbed";
import { StyledBox } from "./index.style";
Expand Down Expand Up @@ -32,9 +32,9 @@ const Home: NextPage = () => {
</Typography>

<Box display="flex" margin="0.75rem">
<Link href="/solve" passHref>
<Button variant="contained">Solve</Button>
</Link>
<Button variant="contained" LinkComponent={NextLink} href="/solve">
Solve
</Button>
<Button
variant="outlined"
endIcon={<ArrowDownward />}
Expand Down

0 comments on commit bf6ac1c

Please sign in to comment.