Next.Js에서 활성 링크를 대상으로 지정하고 스타일을 지정하는 방법(Typescript 사용)

대부분의 React 프로젝트는 React 라우터activeClassName를 사용하여 활성 경로를 대상으로 합니다. 그러나 스타일이 지정된 활성 링크를 사용하여 Next.JS에서 탐색 구성 요소를 빌드하는 것은 간단하지 않습니다.

Next.js에서 유사한 효과를 얻으려면 기본 제공<Link> 구성 요소를 사용자 지정해야 합니다.

Typescript를 사용하는 두 가지 솔루션인 기본 솔루션과 자세한(권장) 솔루션을 살펴보겠습니다.

기본 솔루션



이는 사용자 지정ActiveLink 구성 요소와 useRouter 후크를 사용하는 기본 솔루션의 예입니다.

//Set up your navigation component with a custom 'ActiveLink' component (imported) from a separate file.
// Then create a page route file and component for each 'href' path i.e. index.tsx, about.tsx, products.tsx 

import ActiveLink from './ActiveLink';

const Nav = () => {
  return (
    <nav>
      <ul className="nav">
        <li>
          <ActiveLink href="/">
            Home
          </ActiveLink>
        </li>
        <li>
          <ActiveLink href="/about">
            About
          </ActiveLink>
        </li>
        <li>
          <ActiveLink
            href="/products/"
          >
            Products
          </ActiveLink>
        </li>     
      </ul>
    </nav>
  );
};

export default Nav;




다음으로 활성 링크 동작을 재생성하기 위해 ActiveLink 구성 요소를 빌드해 보겠습니다.

import { useRouter } from 'next/router'
import { LinkProps } from 'next/link';

//LinkProps is a type that requires 'href' as a prop. We're extending it to include a react element as a children prop.
type ActiveLinkProps = LinkProps & {
  children: ReactElement;
}

// href is the url path passed as a prop in the Nav component. The children are the string names passed in-between the ActiveLink tags.
function ActiveLink({ children, href }: ActiveLinkProps) {

// Deconstruct `asPath` from the router object to access the current page path shown in your browser (including the search params).
  const {asPath} = useRouter()

  //define the styling for the active link. If the current page path matches the 'href' path provided, display a red link. All other links will be black.
  const style = {
    color: asPath === href ? 'red' : 'black',
  }

  // Navigate to the page provided as 'href' when the link is clicked (router.push is used for client-side transitions)
  const handleClick = (e) => {
    e.preventDefault()
    router.push(href)
  }

  //the active link will have a style of 'color:red' 
  return (
    <a href={href} onClick={handleClick} style={style}>
      {children}
    </a>
  )
}

export default ActiveLink


이것은 괜찮은 해결책입니다. 하지만 서버 측 렌더링, 동적 경로, 맞춤 링크 소품 등을 포함하도록 앱을 확장하려면 어떻게 해야 할까요?

다음은 ActiveLink 구성 요소에 대한 추가 조정입니다.

권장 솔루션



먼저 Nav 구성 요소에서 activeClassName 문자열이 있는 active 소품을 각 페이지 경로의 ActiveLink 구성 요소에 추가합니다.

또한/products, 즉/products/categories 내에 페이지를 중첩하기 위한 동적 "포괄"경로를 추가할 수 있습니다. 다음과 같이 페이지 폴더에 해당 페이지 경로를 생성해야 합니다.
  • 페이지
  • 제품
  • [...slug]//모든 "포괄"경로에 대한 기본 페이지
  • index.tsx///products의 기본 홈 페이지



  • 
    import ActiveLink from './ActiveLink';
    
    const Nav = () => {
      return (
        <nav>
          <ul className="nav">
            <li>
              <ActiveLink activeClassName="active" href="/">
                <a>Home</a>
              </ActiveLink>
            </li>
            .....
            //add the 'activeClassName' to each ActiveLink as shown in the previous section.
           ...... 
    
           // this is an example of a dynamic route using query paramaters.
            <li>
              <ActiveLink
                activeClassName="active"
                href="/products/[...slug]"
                as="/products/categories?limit=5"
              >
                <a>Products Categories </a>
              </ActiveLink>
            </li>
          </ul>
        </nav>
      );
    };
    
    export default Nav;
    
    


    둘째, activeClassName prop과 나중에 전달할 수 있는 추가 prop을 고려하여 ActiveLink 구성 요소를 수정하겠습니다.

    또한 페이지가 서버 측 렌더링을 사용하여 렌더링되는 경우 asPath 후크의 useRouter가 경로 불일치로 이어지지 않도록 해야 합니다.

    이를 방지하기 위해 Next.js 문서에서는 isReady 사용을 권장합니다. 라우터 필드가 클라이언트 측에서 업데이트되는지 여부를 확인하는 데 사용되는 부울입니다.

    import { useRouter } from 'next/router';
    import Link, { LinkProps } from 'next/link';
    import React, { useState, useEffect, ReactElement, Children } from 'react';
    
    //Add the activeClassName as a required prop
    type ActiveLinkProps = LinkProps & {
      children: ReactElement;
      activeClassName: string;
    };
    
    const ActiveLink = ({
      children,
      activeClassName,
      ...props
    }: ActiveLinkProps) => {
    
      //deconstruct 'isReady' from the useRouter hook.
      const { asPath, isReady } = useRouter();
    
      //create an empty string as the default className of the component
      const [className, setClassName] = useState('');
    
      useEffect(() => {
        // isReady checks if the router fields are updated client-side (it must be used inside a useEffect hook)
        if (isReady) {
    
          // URL().pathname will help to eliminate query and hash strings from the url. 
          // Props.as targets dynamic routes, whilst props.href targets normal static page routes.
    
          const linkPathname = new URL(
            (props.as || props.href) as string,
            location.href
          ).pathname;
    
          // Here we make use of 'asPath' in the correct context (once 'isReady' is true)
          const activePathname = new URL(asPath, location.href).pathname;
    
    
          // Attach the activeClassName to the matching current page 
          const newClassName =
            linkPathname === activePathname
              ? `${activeClassName}`: '';
    
          // Sets a new 'className' state if there is a mismatch between the current and previous state. This ensures a 'toggle' like behavior between link changes.
          if (newClassName !== className) {
            setClassName(newClassName);
          }
        }
    // useEffect dependencies defined below
      }, [
        asPath,
        isReady,
        props.as,
        props.href,
        activeClassName,
        setClassName,
        className,
      ]);
    
      return (
         // return the in-built Next Link including a child (a clone the 'a' element (child) including the activeClassName if it is the active page)
        <Link {...props}>
          {React.cloneElement(child, {
            className: className || null,
          })}
        </Link>
      );
    };
    
    export default ActiveLink;
    
    
    
    


    마지막으로 전역 css 스타일시트의 .active에 스타일을 추가합니다(일반적으로 _app tsx로 가져옴).

    
    .active {
      color: red;
    }
    
    .active:after {
      content: ' (current page)';
    }
    
    


    당신은 이런 것을 봐야합니다 ...


    요약



    Next.Js에서 활성 링크를 대상으로 지정하고 스타일을 지정하는 간단한 솔루션은 useRouter 후크를 활용하여 현재 경로에 액세스하고 activeClassName가 있는 Link 구성 요소를 반환하는 사용자 지정 Link 구성 요소를 만드는 것입니다.

    activeClassName는 CSS를 통해 스타일을 지정하여 페이지 경로의 활성 링크를 표시할 수 있습니다.

    좋은 웹페이지 즐겨찾기