diff --git a/src/Link.tsx b/src/Link.tsx
index b5f90fbc..4beddcd1 100644
--- a/src/Link.tsx
+++ b/src/Link.tsx
@@ -1,116 +1,127 @@
import useEventCallback from '@restart/hooks/useEventCallback';
-import React from 'react';
+import React, { forwardRef } from 'react';
import warning from 'tiny-warning';
import { LinkInjectedProps, LinkProps } from './typeUtils';
import useRouter from './useRouter';
// TODO: Try to type this & simplify those types in next breaking change.
-function Link({
- as: Component = 'a',
- to,
- activeClassName,
- activeStyle,
- activePropName,
- match: propsMatch,
- router: propsRouter,
- exact = false,
- onClick,
- target,
- children,
- ...props
-}: any) {
- const { router, match } = useRouter() || {
- match: propsMatch,
- router: propsRouter,
- };
-
- const handleClick = useEventCallback((event) => {
- if (onClick) {
- onClick(event);
- }
-
- // Don't do anything if the user's onClick handler prevented default.
- // Otherwise, let the browser handle the link with the computed href if the
- // event wasn't an unmodified left click, or if the link has a target other
- // than _self.
- if (
- event.defaultPrevented ||
- event.metaKey ||
- event.altKey ||
- event.ctrlKey ||
- event.shiftKey ||
- event.button !== 0 ||
- (target && target !== '_self')
- ) {
- return;
- }
-
- event.preventDefault();
-
- // FIXME: When clicking on a link to the same location in the browser, the
- // actual becomes a replace rather than a push. We may want the same
- // handling – perhaps implemented in the Farce protocol.
- router.push(to);
- });
+const Link = forwardRef(
+ (
+ {
+ as: Component = 'a',
+ to,
+ activeClassName,
+ activeStyle,
+ activePropName,
+ match: propsMatch,
+ router: propsRouter,
+ exact = false,
+ onClick,
+ target,
+ children,
+ ...props
+ }: any,
+ ref,
+ ) => {
+ const { router, match } = useRouter() || {
+ match: propsMatch,
+ router: propsRouter,
+ };
+
+ const handleClick = useEventCallback((event) => {
+ if (onClick) {
+ onClick(event);
+ }
- if (__DEV__ && typeof Component !== 'function') {
- for (const wrongPropName of ['component', 'Component'] as const) {
- const wrongPropValue = (props as any)[wrongPropName];
- if (!wrongPropValue) {
- continue;
+ // Don't do anything if the user's onClick handler prevented default.
+ // Otherwise, let the browser handle the link with the computed href if the
+ // event wasn't an unmodified left click, or if the link has a target other
+ // than _self.
+ if (
+ event.defaultPrevented ||
+ event.metaKey ||
+ event.altKey ||
+ event.ctrlKey ||
+ event.shiftKey ||
+ event.button !== 0 ||
+ (target && target !== '_self')
+ ) {
+ return;
}
- warning(
- false,
- `Link to ${JSON.stringify(to)} with \`${wrongPropName}\` prop \`${
- wrongPropValue.displayName || wrongPropValue.name || 'UNKNOWN'
- }\` has an element type that is not a component. The expected prop for the link component is \`as\`.`,
- );
+ event.preventDefault();
+
+ // FIXME: When clicking on a link to the same location in the browser, the
+ // actual becomes a replace rather than a push. We may want the same
+ // handling – perhaps implemented in the Farce protocol.
+ router.push(to);
+ });
+
+ if (__DEV__ && typeof Component !== 'function') {
+ for (const wrongPropName of ['component', 'Component'] as const) {
+ const wrongPropValue = (props as any)[wrongPropName];
+ if (!wrongPropValue) {
+ continue;
+ }
+
+ warning(
+ false,
+ `Link to ${JSON.stringify(to)} with \`${wrongPropName}\` prop \`${
+ wrongPropValue.displayName || wrongPropValue.name || 'UNKNOWN'
+ }\` has an element type that is not a component. The expected prop for the link component is \`as\`.`,
+ );
+ }
}
- }
- const href = router.createHref(to);
- const childrenIsFunction = typeof children === 'function';
+ const href = router.createHref(to);
+ const childrenIsFunction = typeof children === 'function';
- if (childrenIsFunction || activeClassName || activeStyle || activePropName) {
- const toLocation = router.createLocation(to);
- const active = router.isActive(match!, toLocation, { exact });
+ if (
+ childrenIsFunction ||
+ activeClassName ||
+ activeStyle ||
+ activePropName
+ ) {
+ const toLocation = router.createLocation(to);
+ const active = router.isActive(match!, toLocation, { exact });
- if (childrenIsFunction) {
- const add = { href, active, onClick: handleClick };
- return children(add);
- }
+ if (childrenIsFunction) {
+ const add = { href, active, onClick: handleClick };
+ return children(add);
+ }
- if (active) {
- if (activeClassName) {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- props.className = props.className
- ? `${props.className} ${activeClassName}`
- : activeClassName;
+ if (active) {
+ if (activeClassName) {
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ props.className = props.className
+ ? `${props.className} ${activeClassName}`
+ : activeClassName;
+ }
+
+ if (activeStyle) {
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ props.style = { ...props.style, ...activeStyle };
+ }
}
- if (activeStyle) {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- props.style = { ...props.style, ...activeStyle };
+ if (activePropName) {
+ props[activePropName] = active;
}
}
- if (activePropName) {
- props[activePropName] = active;
- }
- }
-
- return (
-
- {children}
-
- );
-}
+ return (
+
+ {children}
+
+ );
+ },
+);
// eslint-disable-next-line react/prefer-stateless-function
declare class LinkType<