rsnext/packages/next/client/route-announcer.tsx
Shu Ding 468833e0b4
Fix <RouteAnnouncer/> shouldn't announce initial path under strict mode and React 18 (#34338)
As the comment mentioned, React 18 with Strict Mode enabled might cause double invocation of lifecycle methods. This makes the `<RouteAnnouncer/>` being non-empty for the initial page, which is a bug (it should only announce page change whenever a navigation happens).

## Bug

- [ ] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Errors have helpful link attached, see `contributing.md`

## Feature

- [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have helpful link attached, see `contributing.md`

## Documentation / Examples

- [ ] Make sure the linting passes by running `yarn lint`
2022-02-14 19:18:39 +00:00

62 lines
1.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React from 'react'
import { useRouter } from './router'
export function RouteAnnouncer() {
const { asPath } = useRouter()
const [routeAnnouncement, setRouteAnnouncement] = React.useState('')
// Only announce the path change, but not for the first load because screen
// reader will do that automatically.
const previouslyLoadedPath = React.useRef(asPath)
// Every time the path changes, announce the new pages title following this
// priority: first the document title (from head), otherwise the first h1, or
// if none of these exist, then the pathname from the URL. This methodology is
// inspired by Marcy Suttons accessible client routing user testing. More
// information can be found here:
// https://www.gatsbyjs.com/blog/2019-07-11-user-testing-accessible-client-routing/
React.useEffect(
() => {
// If the path hasn't change, we do nothing.
if (previouslyLoadedPath.current === asPath) return
previouslyLoadedPath.current = asPath
if (document.title) {
setRouteAnnouncement(document.title)
} else {
const pageHeader = document.querySelector('h1')
const content = pageHeader?.innerText ?? pageHeader?.textContent
setRouteAnnouncement(content || asPath)
}
},
// TODO: switch to pathname + query object of dynamic route requirements
[asPath]
)
return (
<p
aria-live="assertive" // Make the announcement immediately.
id="__next-route-announcer__"
role="alert"
style={{
border: 0,
clip: 'rect(0 0 0 0)',
height: '1px',
margin: '-1px',
overflow: 'hidden',
padding: 0,
position: 'absolute',
width: '1px',
// https://medium.com/@jessebeach/beware-smushed-off-screen-accessible-text-5952a4c2cbfe
whiteSpace: 'nowrap',
wordWrap: 'normal',
}}
>
{routeAnnouncement}
</p>
)
}
export default RouteAnnouncer