Skip to content

Commit

Permalink
Update examples/active-class-name (#34205)
Browse files Browse the repository at this point in the history
## 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: #34144
Fixes: #34016
Fixes: #34197
  • Loading branch information
xhoantran committed Feb 11, 2022
1 parent 09443fc commit a38e144
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 8 deletions.
42 changes: 34 additions & 8 deletions 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 (
<Link {...props}>
Expand Down
5 changes: 5 additions & 0 deletions examples/active-class-name/components/Nav.js
Expand Up @@ -22,6 +22,11 @@ const Nav = () => (
<a className="nav-link">About</a>
</ActiveLink>
</li>
<li>
<ActiveLink activeClassName="active" href="/blog">
<a className="nav-link">Blog</a>
</ActiveLink>
</li>
<li>
<ActiveLink activeClassName="active" href="/[slug]" as="/dynamic-route">
<a className="nav-link">Dynamic Route</a>
Expand Down
10 changes: 10 additions & 0 deletions examples/active-class-name/next.config.js
@@ -0,0 +1,10 @@
module.exports = {
async rewrites() {
return [
{
source: '/blog',
destination: '/news',
},
]
},
}
10 changes: 10 additions & 0 deletions examples/active-class-name/pages/news.js
@@ -0,0 +1,10 @@
import Nav from '../components/Nav'

const News = () => (
<>
<Nav />
<p>Hello, I'm the news page</p>
</>
)

export default News

0 comments on commit a38e144

Please sign in to comment.