From a38e1440999748e81b421298407746e5c742a9b8 Mon Sep 17 00:00:00 2001 From: xhoantran <76760622+xhoantran@users.noreply.github.com> Date: Thu, 10 Feb 2022 19:51:56 -0500 Subject: [PATCH] Update examples/active-class-name (#34205) ## Description According to `Next.js` useRouter documentation: - `asPath`: The path (including the query) shown in the browser without the configured basePath or locale. - `pathname`: Current route. That is the path of the page in /pages, the configured basePath or locale is not included. `asPath` should not be used as the props of components. There are many cases that `asPath` not working as expected. For example: - `asPath` is different on server-side and client-side. - `asPath` can contains `id` and `query`. ## Suggestion - Warning the use of `asPath` can lead to the conflict of client and server-side. - Update `useRouter` document. ## Bug - [x] Related issues linked using `fixes #number` Fixes: https://github.com/vercel/next.js/issues/34144 Fixes: https://github.com/vercel/next.js/issues/34016 Fixes: https://github.com/vercel/next.js/issues/34197 --- .../components/ActiveLink.js | 42 +++++++++++++++---- examples/active-class-name/components/Nav.js | 5 +++ examples/active-class-name/next.config.js | 10 +++++ examples/active-class-name/pages/news.js | 10 +++++ 4 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 examples/active-class-name/next.config.js create mode 100644 examples/active-class-name/pages/news.js diff --git a/examples/active-class-name/components/ActiveLink.js b/examples/active-class-name/components/ActiveLink.js index 0db59b6a9227a..7ada49c52056e 100644 --- a/examples/active-class-name/components/ActiveLink.js +++ b/examples/active-class-name/components/ActiveLink.js @@ -1,20 +1,46 @@ +import { useState, useEffect } from 'react' 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 { asPath, isReady } = useRouter() + const child = Children.only(children) const childClassName = child.props.className || '' + const [className, setClassName] = useState(childClassName) + + useEffect(() => { + // Check if the router fields are updated client-side + if (isReady) { + // Dynamic route will be matched via props.as + // Static route will be matched via props.href + const linkPathname = new URL(props.as || props.href, location.href) + .pathname + + // Using URL().pathname to get rid of query and hash + const activePathname = new URL(asPath, location.href).pathname + + const newClassName = + linkPathname === activePathname + ? `${childClassName} ${activeClassName}`.trim() + : childClassName - // 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 + if (newClassName !== className) { + setClassName(newClassName) + } + } + }, [ + asPath, + isReady, + props.as, + props.href, + childClassName, + activeClassName, + setClassName, + className, + ]) return ( diff --git a/examples/active-class-name/components/Nav.js b/examples/active-class-name/components/Nav.js index 2c710bb42468f..921cc15120bfd 100644 --- a/examples/active-class-name/components/Nav.js +++ b/examples/active-class-name/components/Nav.js @@ -22,6 +22,11 @@ const Nav = () => ( About +
Hello, I'm the news page
+ > +) + +export default News