From bb9a6877ad44f5ee4956e8c434b3b62bfc01d6b9 Mon Sep 17 00:00:00 2001 From: Adam Pietrasiak Date: Tue, 3 Aug 2021 13:19:18 +0200 Subject: [PATCH 1/2] Require component rendered as child of `Link` to pass event to `onClick` handler 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 ``` --- packages/next/client/link.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/next/client/link.tsx b/packages/next/client/link.tsx index 9419739564ee872..2aa2212d00ee0ed 100644 --- a/packages/next/client/link.tsx +++ b/packages/next/client/link.tsx @@ -282,6 +282,11 @@ function Link(props: React.PropsWithChildren) { } = { ref: setRef, onClick: (e: React.MouseEvent) => { + if (!e) { + if (process.env.NODE_ENV !== 'production') { + throw new Error(`Component rendered inside Link has to pass click event to "onClick" prop.`) + } + } if (child.props && typeof child.props.onClick === 'function') { child.props.onClick(e) } From cf44f9b2ab77ee61d6169d84613f6f14b1bc8a36 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Sun, 6 Feb 2022 17:29:25 -0600 Subject: [PATCH 2/2] Update check and add test --- packages/next/client/link.tsx | 8 +++-- .../pages/link-invalid-onclick.js | 35 +++++++++++++++++++ .../client-navigation/test/index.test.js | 7 ++++ 3 files changed, 47 insertions(+), 3 deletions(-) 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 aa07382e4d318b1..9a7f65b52997b92 100644 --- a/packages/next/client/link.tsx +++ b/packages/next/client/link.tsx @@ -284,9 +284,11 @@ function Link(props: React.PropsWithChildren) { } = { ref: setRef, onClick: (e: React.MouseEvent) => { - if (!e) { - if (process.env.NODE_ENV !== 'production') { - throw new Error(`Component rendered inside Link has to pass click event to "onClick" prop.`) + 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') { 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}

+ +