From 023a01ff0093454e39bfd8fb29e3c132fdb3c2a0 Mon Sep 17 00:00:00 2001 From: Adam Pietrasiak Date: Mon, 7 Feb 2022 01:04:37 +0100 Subject: [PATCH] Require component rendered as child of `Link` to pass event to `onClick` handler (#27723) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, if you render `next/link` like ```ts ``` and have ```tsx interface Props { onClick?: () => void; // <— note we're not passing event as an argument } function CustomComponent({ onClick }: Props) { return
onClick?.()}>Hello
} ``` It'll result in error ``` link.js?f421:21 Uncaught TypeError: Cannot read property 'defaultPrevented' of undefined ``` ## 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> --- packages/next/client/link.tsx | 7 ++++ .../pages/link-invalid-onclick.js | 35 +++++++++++++++++++ .../client-navigation/test/index.test.js | 7 ++++ 3 files changed, 49 insertions(+) create mode 100644 test/integration/client-navigation/pages/link-invalid-onclick.js diff --git a/packages/next/client/link.tsx b/packages/next/client/link.tsx index 2aba7f79139d8b8..9a7f65b52997b92 100644 --- a/packages/next/client/link.tsx +++ b/packages/next/client/link.tsx @@ -284,6 +284,13 @@ function Link(props: React.PropsWithChildren) { } = { 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) } diff --git a/test/integration/client-navigation/pages/link-invalid-onclick.js b/test/integration/client-navigation/pages/link-invalid-onclick.js new file mode 100644 index 000000000000000..7cebe86d2895df2 --- /dev/null +++ b/test/integration/client-navigation/pages/link-invalid-onclick.js @@ -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 ( + { + e.preventDefault() + try { + props.onClick() + } catch (err) { + setErrorCount(errorCount + 1) + console.error(err) + } + }} + > + {props.href} + + ) + } + + return ( + <> +

{errorCount}

+ +