next.js 警告:失败的 prop 类型:提供给`ForwardRef(Link)`的prop `component`无效

5fjcxozz  于 2023-10-18  发布在  其他
关注(0)|答案(1)|浏览(102)

我们的NextJS应用程序在prod中运行良好,并且在本地呈现OK,但我们得到了一些丑陋的警告,我试图安静。

client.js:1 Warning: Failed prop type: Invalid prop `component` supplied to `ForwardRef(Link)`. Expected an element type that can hold a ref. Did you accidentally provide a plain function component instead? For more information see https://mui.com/r/caveat-with-refs-guide
    at Link (webpack-internal:///./node_modules/@mui/material/Link/Link.js:111:83)
    at Link (webpack-internal:///./src/components/Link.tsx:29:11)
    at span[...]

Link.tsx看起来像这样:

import { LinkProps, Link as MuiLink } from "@mui/material";
import NextLink, { LinkProps as NextLinkProps } from "next/link";
// Defining the CustomNextLink
export type CustomNextLinkProps = Omit<NextLinkProps, "href"> & {
  _href: NextLinkProps["href"];
};
export const CustomNextLink = ({ _href, ...props }: CustomNextLinkProps) => {
  return <NextLink href={_href} {...props} />;
};
// combine MUI LinkProps with NextLinkProps
type CombinedLinkProps = LinkProps<typeof NextLink>;
// we remove both href properties
// and define a new href property using NextLinkProps
export type MyLinkProps = Omit<CombinedLinkProps, "href"> & {
  href: NextLinkProps["href"];
};
export const Link = ({ href, ...props }: MyLinkProps) => {
  // use _href props of CustomNextLink to set the href
  return <MuiLink {...props} component={CustomNextLink} _href={href} />;
};

我这里有一个repro:https://stackblitz.com/edit/stackblitz-starters-3ucqb2?description=React%20%20%20TypeScript%20starter%20project&file=src%2FLink.tsx,src%2FApp.tsx,src%2Findex.tsx&title=React%20Starter
似乎每当我们使用这个组件时,警告就会出现在控制台中。这个错误的其他示例似乎可以通过将内部组件 Package 在<div>或类似的文件中来解决,但在这里似乎没有帮助。

0vvn1miw

0vvn1miw1#

在这个自定义链接组件中,引用似乎没有被正确转发,这通常是此错误的来源。说实话,将Next的链接与Material UI的链接集成在一起有点残酷,所以我总是使用MUI的官方示例,它没有控制台错误:https://github.com/mui/material-ui/blob/master/examples/material-ui-nextjs-pages-router-ts/src/Link.tsx
它看起来像这样:

import * as React from 'react';
import clsx from 'clsx';
import { useRouter } from 'next/router';
import NextLink, { LinkProps as NextLinkProps } from 'next/link';
import MuiLink, { LinkProps as MuiLinkProps } from '@mui/material/Link';
import { styled } from '@mui/material/styles';

// Add support for the sx prop for consistency with the other branches.
const Anchor = styled('a')({});

interface NextLinkComposedProps
  extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href'>,
    Omit<NextLinkProps, 'href' | 'as' | 'passHref' | 'onMouseEnter' | 'onClick' | 'onTouchStart'> {
  to: NextLinkProps['href'];
  linkAs?: NextLinkProps['as'];
}

export const NextLinkComposed = React.forwardRef<HTMLAnchorElement, NextLinkComposedProps>(
  function NextLinkComposed(props, ref) {
    const {
      to,
      linkAs,
      replace,
      scroll,
      shallow,
      prefetch,
      legacyBehavior = true,
      locale,
      ...other
    } = props;

    return (
      <NextLink
        href={to}
        prefetch={prefetch}
        as={linkAs}
        replace={replace}
        scroll={scroll}
        shallow={shallow}
        passHref
        locale={locale}
        legacyBehavior={legacyBehavior}
      >
        <Anchor ref={ref} {...other} />
      </NextLink>
    );
  },
);

export type LinkProps = {
  activeClassName?: string;
  as?: NextLinkProps['as'];
  href: NextLinkProps['href'];
  linkAs?: NextLinkProps['as']; // Useful when the as prop is shallow by styled().
  noLinkStyle?: boolean;
} & Omit<NextLinkComposedProps, 'to' | 'linkAs' | 'href'> &
  Omit<MuiLinkProps, 'href'>;

// A styled version of the Next.js Link component:
// https://nextjs.org/docs/pages/api-reference/components/link
const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(function Link(props, ref) {
  const {
    activeClassName = 'active',
    as,
    className: classNameProps,
    href,
    legacyBehavior,
    linkAs: linkAsProp,
    locale,
    noLinkStyle,
    prefetch,
    replace,
    role, // Link don't have roles.
    scroll,
    shallow,
    ...other
  } = props;

  const router = useRouter();
  const pathname = typeof href === 'string' ? href : href.pathname;
  const className = clsx(classNameProps, {
    [activeClassName]: router.pathname === pathname && activeClassName,
  });

  const isExternal =
    typeof href === 'string' && (href.indexOf('http') === 0 || href.indexOf('mailto:') === 0);

  if (isExternal) {
    if (noLinkStyle) {
      return <Anchor className={className} href={href} ref={ref} {...other} />;
    }

    return <MuiLink className={className} href={href} ref={ref} {...other} />;
  }

  const linkAs = linkAsProp || as;
  const nextjsProps = {
    to: href,
    linkAs,
    replace,
    scroll,
    shallow,
    prefetch,
    legacyBehavior,
    locale,
  };

  if (noLinkStyle) {
    return <NextLinkComposed className={className} ref={ref} {...nextjsProps} {...other} />;
  }

  return (
    <MuiLink
      component={NextLinkComposed}
      className={className}
      ref={ref}
      {...nextjsProps}
      {...other}
    />
  );
});

export default Link;

希望能帮上忙!

相关问题