当路线在 Next.js 中处于活动状态时,目标活动 Link
Target Active Link when the route is active in Next.js
如何像我们在 React-Router-4 中那样以 Next.js 中的活动 Link 为目标?
意思是,当其路由处于活动状态时,给活动 link 一个 class?
首先,您需要有一个名为 Link 的组件,具有临时属性 activeClassName
import { useRouter } from 'next/router'
import PropTypes from 'prop-types'
import Link from 'next/link'
import React, { Children } from 'react'
const ActiveLink = ({ children, activeClassName, ...props }) => {
const { asPath } = useRouter()
const child = Children.only(children)
const childClassName = child.props.className || ''
// pages/index.js will be matched via props.href
// pages/about.js will be matched via props.href
// pages/[slug].js will be matched via props.as
const className =
asPath === props.href || asPath === props.as
? `${childClassName} ${activeClassName}`.trim()
: childClassName
return (
<Link {...props}>
{React.cloneElement(child, {
className: className || null,
})}
</Link>
)
}
ActiveLink.propTypes = {
activeClassName: PropTypes.string.isRequired,
}
export default ActiveLink
然后有一个导航栏,其中包含创建的组件 Link 和 css 选择器 :active
以区分活动和非活动 link。
import ActiveLink from './ActiveLink'
const Nav = () => (
<nav>
<style jsx>{`
.nav-link {
text-decoration: none;
}
.active:after {
content: ' (current page)';
}
`}</style>
<ul className="nav">
<li>
<ActiveLink activeClassName="active" href="/">
<a className="nav-link">Home</a>
</ActiveLink>
</li>
<li>
<ActiveLink activeClassName="active" href="/about">
<a className="nav-link">About</a>
</ActiveLink>
</li>
<li>
<ActiveLink activeClassName="active" href="/[slug]" as="/dynamic-route">
<a className="nav-link">Dynamic Route</a>
</ActiveLink>
</li>
</ul>
</nav>
)
export default Nav
之后,您可以将导航栏实现到您的页面:
import Nav from '../components/Nav'
export default () => (
<div>
<Nav />
<p>Hello, I'm the home page</p>
</div>
)
这项工作的关键位于组件 Link 内部,我们将 router.pathname
的值与 Link 的属性 href
进行比较,如果值匹配另一个然后输入特定的类名以使 link 看起来已激活。
参考:here
另一个支持 as
属性的最小版本:
import Link from "next/link";
import {withRouter} from "next/router";
import {Children} from "react";
import React from "react";
export default withRouter(({router, children, as, href, ...rest}) => (
<Link {...rest} href={href} as={as}>
{React.cloneElement(Children.only(children), {
className: (router.asPath === href || router.asPath === as) ? `active` : null
})}
</Link>
));
基于 useRouter
钩子的简单解决方案:
import Link from "next/link";
import { useRouter } from "next/router";
export const MyNav = () => {
const router = useRouter();
return (
<ul>
<li className={router.pathname == "/" ? "active" : ""}>
<Link href="/">home</Link>
</li>
<li className={router.pathname == "/about" ? "active" : ""}>
<Link href="/about">about</Link>
</li>
</ul>
);
};
如果您想包含 url 查询参数,您也可以使用 router.asPath
而不是 router.pathname
。如果您想处理诸如 /#about
.
之类的锚标记,这将很有用
如果你想使用锚点Link试试这个版本的@Rotareti 的代码:
import Link from "next/link";
import { useRouter } from "next/router";
export const MyNav = () => {
const router = useRouter();
return (
<ul>
<li className={router.asPath == "/#about" ? "active" : ""}>
<Link href="#about">about</Link>
</li>
</ul>
);
}`;
打字稿版本:
import React from 'react'
import Link, { LinkProps } from 'next/link'
import { useRouter } from 'next/router'
export interface NavLinkProps extends LinkProps {
children: React.ReactElement
}
export function NavLink({ children, href, ...props }: NavLinkProps) {
const router = useRouter()
return (
<Link href={href} {...props}>
{router.pathname === href ? React.cloneElement(children, { 'data-active': true }) : children}
</Link>
)
}
请注意,除非必要,否则我不会克隆 child。
如果存在 URL 参数并检查子页面是否处于活动状态,此解决方案也适用。基于 Darryl RN 和 Saman Mohamadi 的回答
它作为 NextJS link 组件的替代品,如果路由或子页面的路由是 classes “active” 和 “active-sub”活跃。
创建一个名为 Link.js 或任何您喜欢的文件:
import { withRouter } from "next/router";
import Link from "next/link";
import React, { Children } from "react";
export default withRouter(({ router, children, as, href, activeClassName, activeSubClassName, ...rest }) => {
const child = Children.only(children);
const childClassName = child.props.className || "";
// remove URL parameters
const sanitizedPath = router.asPath.split("#")[0].split("?")[0];
// activeClassName and activeSubClassName are optional and default to "active" and "active-sub"
const activeClass = activeClassName || "active";
const activeSubClass = activeSubClassName || "active-sub";
// remove trailing slash if present
href = href && href !== "/" && href.endsWith("/") ? href.slice(0, -1) : href;
as = as && as !== "/" && as.endsWith("/") ? as.slice(0, -1) : as;
// check if the link or a sub-page is active and return the according class name
const activityClassName = sanitizedPath === href || sanitizedPath === as ? activeClass : sanitizedPath.startsWith(href + "/") || sanitizedPath.startsWith(as + "/") ? activeSubClass : "";
// combine the child class names with the activity class name
const className = `${childClassName} ${activityClassName}`.trim();
return (
<Link href={href} as={as} {...rest}>
{React.cloneElement(child, {
className: className || null,
})}
</Link>
);
});
通过
将其导入您的文件
import Link from "./Link.js";
或任何你喜欢的名字
import ActiveLink from "./Link.js";
并像使用 NextJS "Link" 组件一样使用它 (next/link):
<Link href="/home">
<a className="link-classname">Home</a>
</Link>
它将默认为 class 名称“active”和“active-sub”,但您可以设置自定义 class 名称:
<Link href="/home" activeClassName="my-active-classname" activeSubClassName="another-classname">
<a className="link-classname">Home</a>
</Link>
如果您不需要其中一个活动的 classes,请在字符串中添加一个 space:
<Link href="/home" activeSubClassName=" ">
<a className="link-classname">Home</a>
</Link>
只需在其中添加一个标签...
<Link href={href}>
<a className='text-red-400 active:text-red-800'>{children}</a>
</Link>
这是我的解决方案。我将 href
和 asPath
属性标记化,然后循环匹配它们。
您可以选择一个确切的 link(默认)
<ActiveLink href='/events'>
<a href='/page'>Page</a>
</ActiveLink>
或者使用 fuzzy
属性
的模糊 link(匹配/事件)
<ActiveLink fuzzy href='/events/id'>
<a href='/events/id'>Event</a>
</ActiveLink>
这是组件
import React from 'react';
import NextLink from 'next/link';
import { useRouter } from 'next/router';
const ActiveLink = ({ fuzzy = false, href, children }) => {
const router = useRouter();
let className = children.props.className || '';
const hrefTokens = href.substr(1).split('/');
const pathTokens = router.asPath.substr(1).split('/');
let matched = false;
for (let i = 0; i < hrefTokens.length; i++) {
if (hrefTokens[i] === pathTokens[i]) {
matched = true;
break;
}
}
if ((!fuzzy && router.asPath === href) || (fuzzy && matched)) {
className = `${className} active`;
}
return (
<NextLink href={href}>
{React.cloneElement(children, { className })}
</NextLink>
);
};
export default ActiveLink;
这是带有 NextJS 的另一个 ActiveLink 版本(见下图)
import { withRouter } from 'next/router';
import PropTypes from 'prop-types';
import React from 'react';
const ActiveLink = ({ router, href, isLeftSideBar = false, children }) => {
const isCurrentPath = router.pathname === href || router.asPath === href;
const handleClick = (e) => {
e.preventDefault();
router.push(href);
};
(function prefetchPages() {
if (typeof window !== 'undefined') router.prefetch(router.pathname);
})();
const theme =
settings.theme === THEMES.LIGHT && isLeftSideBar ? '#e65100' : '#ffeb3b';
const color = isCurrentPath ? theme : '';
return (
<a
href={href}
onClick={handleClick}
style={{
textDecoration: 'none',
margin: 16,
padding: 0,
fontWeight: isCurrentPath ? 'bold' : 'normal', // I left mine all bold
fontSize: 17,
color: isLeftSideBar ? '#e65100' : '#ffeb3b',
}}>
{children}
</a>
);
};
ActiveLink.propTypes = {
href: PropTypes.string.isRequired,
children: PropTypes.any,
};
export default withRouter(ActiveLink);
随处调用
<ActiveLink href='/signup'> Sign Up </ActiveLink>
结果:
//NavItem Wrapper
import { useRouter } from 'next/router'
import React from 'react'
const ActiveNav = ({ path, children }) => {
const router = useRouter();
const className = router.asPath === `/${path}` ? "active" : '';
return (
<div className={className}>
{children}
</div>
)
}
export default ActiveNav
// 在另一个文件中
import NavbarItem from 'path of ActiveNav component';
const { Header, Content, Footer } = Layout;
const LayoutComponent = (props) => {
return (
<>
<nav className="navigation">
<NavbarItem path="">
<div className="nav-items">
<Link href="/">
<a>Home</a>
</Link>
</div>
</NavbarItem>
<NavbarItem path="category/game">
<div className="nav-items">
<Link href="/category/game">
<a>Game</a>
</Link>
</div>
</NavbarItem>
</nav>
<>
)
}
export default LayoutComponent
add the style file and import it (Globally or in the Active Nav component)
.navigation > .active{
color:green;
font:bold;
// customize according to need
}
我在 typescript 中创建了一个组件
import { UrlObject } from "url";
interface ActiveLinkProps {
activeClassName?: string;
href: string | UrlObject;
}
// children is the <a>, prop is the "href"
const ActiveLink: React.FC<ActiveLinkProps> = ({ children, ...props }) => {
const router = useRouter();
// this will make sure i m passing only one child so i will access the its props easily
const child = Children.only(children) as React.ReactElement;
let className = child.props ? child.props.className : "";
if (router.asPath === props.href && props.activeClassName) {
className = `${className} ${props.activeClassName}`;
}
delete props.activeClassName;
return (
<Link href={props.href}>{React.cloneElement(child, { className })}</Link>
);
};
然后像这样使用它
<ActiveLink activeClassName="active" href={href}>
<a className={`nav-link port-navbar-link ${className}`}>{title}</a>
</ActiveLink>
如何像我们在 React-Router-4 中那样以 Next.js 中的活动 Link 为目标? 意思是,当其路由处于活动状态时,给活动 link 一个 class?
首先,您需要有一个名为 Link 的组件,具有临时属性 activeClassName
import { useRouter } from 'next/router'
import PropTypes from 'prop-types'
import Link from 'next/link'
import React, { Children } from 'react'
const ActiveLink = ({ children, activeClassName, ...props }) => {
const { asPath } = useRouter()
const child = Children.only(children)
const childClassName = child.props.className || ''
// pages/index.js will be matched via props.href
// pages/about.js will be matched via props.href
// pages/[slug].js will be matched via props.as
const className =
asPath === props.href || asPath === props.as
? `${childClassName} ${activeClassName}`.trim()
: childClassName
return (
<Link {...props}>
{React.cloneElement(child, {
className: className || null,
})}
</Link>
)
}
ActiveLink.propTypes = {
activeClassName: PropTypes.string.isRequired,
}
export default ActiveLink
然后有一个导航栏,其中包含创建的组件 Link 和 css 选择器 :active
以区分活动和非活动 link。
import ActiveLink from './ActiveLink'
const Nav = () => (
<nav>
<style jsx>{`
.nav-link {
text-decoration: none;
}
.active:after {
content: ' (current page)';
}
`}</style>
<ul className="nav">
<li>
<ActiveLink activeClassName="active" href="/">
<a className="nav-link">Home</a>
</ActiveLink>
</li>
<li>
<ActiveLink activeClassName="active" href="/about">
<a className="nav-link">About</a>
</ActiveLink>
</li>
<li>
<ActiveLink activeClassName="active" href="/[slug]" as="/dynamic-route">
<a className="nav-link">Dynamic Route</a>
</ActiveLink>
</li>
</ul>
</nav>
)
export default Nav
之后,您可以将导航栏实现到您的页面:
import Nav from '../components/Nav'
export default () => (
<div>
<Nav />
<p>Hello, I'm the home page</p>
</div>
)
这项工作的关键位于组件 Link 内部,我们将 router.pathname
的值与 Link 的属性 href
进行比较,如果值匹配另一个然后输入特定的类名以使 link 看起来已激活。
参考:here
另一个支持 as
属性的最小版本:
import Link from "next/link";
import {withRouter} from "next/router";
import {Children} from "react";
import React from "react";
export default withRouter(({router, children, as, href, ...rest}) => (
<Link {...rest} href={href} as={as}>
{React.cloneElement(Children.only(children), {
className: (router.asPath === href || router.asPath === as) ? `active` : null
})}
</Link>
));
基于 useRouter
钩子的简单解决方案:
import Link from "next/link";
import { useRouter } from "next/router";
export const MyNav = () => {
const router = useRouter();
return (
<ul>
<li className={router.pathname == "/" ? "active" : ""}>
<Link href="/">home</Link>
</li>
<li className={router.pathname == "/about" ? "active" : ""}>
<Link href="/about">about</Link>
</li>
</ul>
);
};
如果您想包含 url 查询参数,您也可以使用 router.asPath
而不是 router.pathname
。如果您想处理诸如 /#about
.
如果你想使用锚点Link试试这个版本的@Rotareti 的代码:
import Link from "next/link";
import { useRouter } from "next/router";
export const MyNav = () => {
const router = useRouter();
return (
<ul>
<li className={router.asPath == "/#about" ? "active" : ""}>
<Link href="#about">about</Link>
</li>
</ul>
);
}`;
打字稿版本:
import React from 'react'
import Link, { LinkProps } from 'next/link'
import { useRouter } from 'next/router'
export interface NavLinkProps extends LinkProps {
children: React.ReactElement
}
export function NavLink({ children, href, ...props }: NavLinkProps) {
const router = useRouter()
return (
<Link href={href} {...props}>
{router.pathname === href ? React.cloneElement(children, { 'data-active': true }) : children}
</Link>
)
}
请注意,除非必要,否则我不会克隆 child。
如果存在 URL 参数并检查子页面是否处于活动状态,此解决方案也适用。基于 Darryl RN 和 Saman Mohamadi 的回答
它作为 NextJS link 组件的替代品,如果路由或子页面的路由是 classes “active” 和 “active-sub”活跃。
创建一个名为 Link.js 或任何您喜欢的文件:
import { withRouter } from "next/router";
import Link from "next/link";
import React, { Children } from "react";
export default withRouter(({ router, children, as, href, activeClassName, activeSubClassName, ...rest }) => {
const child = Children.only(children);
const childClassName = child.props.className || "";
// remove URL parameters
const sanitizedPath = router.asPath.split("#")[0].split("?")[0];
// activeClassName and activeSubClassName are optional and default to "active" and "active-sub"
const activeClass = activeClassName || "active";
const activeSubClass = activeSubClassName || "active-sub";
// remove trailing slash if present
href = href && href !== "/" && href.endsWith("/") ? href.slice(0, -1) : href;
as = as && as !== "/" && as.endsWith("/") ? as.slice(0, -1) : as;
// check if the link or a sub-page is active and return the according class name
const activityClassName = sanitizedPath === href || sanitizedPath === as ? activeClass : sanitizedPath.startsWith(href + "/") || sanitizedPath.startsWith(as + "/") ? activeSubClass : "";
// combine the child class names with the activity class name
const className = `${childClassName} ${activityClassName}`.trim();
return (
<Link href={href} as={as} {...rest}>
{React.cloneElement(child, {
className: className || null,
})}
</Link>
);
});
通过
将其导入您的文件import Link from "./Link.js";
或任何你喜欢的名字
import ActiveLink from "./Link.js";
并像使用 NextJS "Link" 组件一样使用它 (next/link):
<Link href="/home">
<a className="link-classname">Home</a>
</Link>
它将默认为 class 名称“active”和“active-sub”,但您可以设置自定义 class 名称:
<Link href="/home" activeClassName="my-active-classname" activeSubClassName="another-classname">
<a className="link-classname">Home</a>
</Link>
如果您不需要其中一个活动的 classes,请在字符串中添加一个 space:
<Link href="/home" activeSubClassName=" ">
<a className="link-classname">Home</a>
</Link>
只需在其中添加一个标签...
<Link href={href}>
<a className='text-red-400 active:text-red-800'>{children}</a>
</Link>
这是我的解决方案。我将 href
和 asPath
属性标记化,然后循环匹配它们。
您可以选择一个确切的 link(默认)
<ActiveLink href='/events'>
<a href='/page'>Page</a>
</ActiveLink>
或者使用 fuzzy
属性
<ActiveLink fuzzy href='/events/id'>
<a href='/events/id'>Event</a>
</ActiveLink>
这是组件
import React from 'react';
import NextLink from 'next/link';
import { useRouter } from 'next/router';
const ActiveLink = ({ fuzzy = false, href, children }) => {
const router = useRouter();
let className = children.props.className || '';
const hrefTokens = href.substr(1).split('/');
const pathTokens = router.asPath.substr(1).split('/');
let matched = false;
for (let i = 0; i < hrefTokens.length; i++) {
if (hrefTokens[i] === pathTokens[i]) {
matched = true;
break;
}
}
if ((!fuzzy && router.asPath === href) || (fuzzy && matched)) {
className = `${className} active`;
}
return (
<NextLink href={href}>
{React.cloneElement(children, { className })}
</NextLink>
);
};
export default ActiveLink;
这是带有 NextJS 的另一个 ActiveLink 版本(见下图)
import { withRouter } from 'next/router';
import PropTypes from 'prop-types';
import React from 'react';
const ActiveLink = ({ router, href, isLeftSideBar = false, children }) => {
const isCurrentPath = router.pathname === href || router.asPath === href;
const handleClick = (e) => {
e.preventDefault();
router.push(href);
};
(function prefetchPages() {
if (typeof window !== 'undefined') router.prefetch(router.pathname);
})();
const theme =
settings.theme === THEMES.LIGHT && isLeftSideBar ? '#e65100' : '#ffeb3b';
const color = isCurrentPath ? theme : '';
return (
<a
href={href}
onClick={handleClick}
style={{
textDecoration: 'none',
margin: 16,
padding: 0,
fontWeight: isCurrentPath ? 'bold' : 'normal', // I left mine all bold
fontSize: 17,
color: isLeftSideBar ? '#e65100' : '#ffeb3b',
}}>
{children}
</a>
);
};
ActiveLink.propTypes = {
href: PropTypes.string.isRequired,
children: PropTypes.any,
};
export default withRouter(ActiveLink);
随处调用
<ActiveLink href='/signup'> Sign Up </ActiveLink>
结果:
//NavItem Wrapper
import { useRouter } from 'next/router'
import React from 'react'
const ActiveNav = ({ path, children }) => {
const router = useRouter();
const className = router.asPath === `/${path}` ? "active" : '';
return (
<div className={className}>
{children}
</div>
)
}
export default ActiveNav
// 在另一个文件中
import NavbarItem from 'path of ActiveNav component';
const { Header, Content, Footer } = Layout;
const LayoutComponent = (props) => {
return (
<>
<nav className="navigation">
<NavbarItem path="">
<div className="nav-items">
<Link href="/">
<a>Home</a>
</Link>
</div>
</NavbarItem>
<NavbarItem path="category/game">
<div className="nav-items">
<Link href="/category/game">
<a>Game</a>
</Link>
</div>
</NavbarItem>
</nav>
<>
)
}
export default LayoutComponent
add the style file and import it (Globally or in the Active Nav component)
.navigation > .active{
color:green;
font:bold;
// customize according to need
}
我在 typescript 中创建了一个组件
import { UrlObject } from "url";
interface ActiveLinkProps {
activeClassName?: string;
href: string | UrlObject;
}
// children is the <a>, prop is the "href"
const ActiveLink: React.FC<ActiveLinkProps> = ({ children, ...props }) => {
const router = useRouter();
// this will make sure i m passing only one child so i will access the its props easily
const child = Children.only(children) as React.ReactElement;
let className = child.props ? child.props.className : "";
if (router.asPath === props.href && props.activeClassName) {
className = `${className} ${props.activeClassName}`;
}
delete props.activeClassName;
return (
<Link href={props.href}>{React.cloneElement(child, { className })}</Link>
);
};
然后像这样使用它
<ActiveLink activeClassName="active" href={href}>
<a className={`nav-link port-navbar-link ${className}`}>{title}</a>
</ActiveLink>