Require component rendered as child of Link
to pass event to onClick
handler (#27723)
Currently, if you render `next/link` like ```ts <Link> <CustomComponent /> </Link> ``` and have ```tsx interface Props { onClick?: () => void; // <— note we're not passing event as an argument } function CustomComponent({ onClick }: Props) { return <div onClick={() => onClick?.()}>Hello</div> } ``` It'll result in error ``` link.js?f421:21 Uncaught TypeError: Cannot read property 'defaultPrevented' of undefined ``` <!-- Thanks for opening a PR! Your contribution is much appreciated. In order to make sure your PR is handled as smoothly as possible we request that you follow the checklist sections below. Choose the right checklist for the change that you're making: --> ## Bug - [ ] Related issues linked using `fixes #number` - [ ] 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 Co-authored-by: JJ Kasper <22380829+ijjk@users.noreply.github.com>
This commit is contained in:
parent
91146b23a2
commit
41706d6fd3
3 changed files with 49 additions and 0 deletions
|
@ -284,6 +284,13 @@ function Link(props: React.PropsWithChildren<LinkProps>) {
|
|||
} = {
|
||||
ref: setRef,
|
||||
onClick: (e: React.MouseEvent) => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (!e) {
|
||||
throw new Error(
|
||||
`Component rendered inside next/link has to pass click event to "onClick" prop.`
|
||||
)
|
||||
}
|
||||
}
|
||||
if (child.props && typeof child.props.onClick === 'function') {
|
||||
child.props.onClick(e)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import Link from 'next/link'
|
||||
import { useState } from 'react'
|
||||
|
||||
export default function Page(props) {
|
||||
const [errorCount, setErrorCount] = useState(0)
|
||||
|
||||
function Button(props) {
|
||||
return (
|
||||
<a
|
||||
id="custom-button"
|
||||
href={props.href}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
try {
|
||||
props.onClick()
|
||||
} catch (err) {
|
||||
setErrorCount(errorCount + 1)
|
||||
console.error(err)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{props.href}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="errors">{errorCount}</p>
|
||||
<Link href="/nav" passHref>
|
||||
<Button />
|
||||
</Link>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -87,6 +87,13 @@ describe('Client Navigation', () => {
|
|||
expect(text).toMatch(/this is the about page/i)
|
||||
})
|
||||
|
||||
it('should error when calling onClick without event', async () => {
|
||||
const browser = await webdriver(context.appPort, '/link-invalid-onclick')
|
||||
expect(await browser.elementByCss('#errors').text()).toBe('0')
|
||||
await browser.elementByCss('#custom-button').click()
|
||||
expect(await browser.elementByCss('#errors').text()).toBe('1')
|
||||
})
|
||||
|
||||
it('should navigate via the client side', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
|
||||
|
|
Loading…
Reference in a new issue