Из документации NextJS я узнал, что мы можем использовать объект URL, и он будет автоматически отформатирован для создания строки URL.
Функционально работает без проблем, но в консоли появляется следующая ошибка:
Warning: Failed prop type: Invalid prop `href` of type `object` supplied to `ForwardRef(ButtonBase)`, expected `string`.
Вот минимальная репродукция CodeSandbox: https://codesandbox.io/s/next-materialui-objecturl-rmprr?file=/pages/index.js
// index.js
import React from "react";
import Button from "@material-ui/core/Button";
import Link from "../src/Link";
export default function Index() {
return (
<div>
<Button
component = {Link}
href = {{
pathname: "/about",
query: { name: "test" }
}}
naked
variant = "contained"
>
Link button with url object
</Button>
</div>
);
}
// Link.js
import React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { useRouter } from 'next/router';
import NextLink from 'next/link';
import MuiLink from '@material-ui/core/Link';
const NextComposed = React.forwardRef(function NextComposed(props, ref) {
const { as, href, ...other } = props;
return (
<NextLink href = {href} as = {as}>
<a ref = {ref} {...other} />
</NextLink>
);
});
NextComposed.propTypes = {
as: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
href: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
prefetch: PropTypes.bool,
};
// A styled version of the Next.js Link component:
// https://nextjs.org/docs/#with-link
function Link(props) {
const {
href,
activeClassName = 'active',
className: classNameProps,
innerRef,
naked,
...other
} = props;
const router = useRouter();
const pathname = typeof href === 'string' ? href : href.pathname;
const className = clsx(classNameProps, {
[activeClassName]: router.pathname === pathname && activeClassName,
});
if (naked) {
return <NextComposed className = {className} ref = {innerRef} href = {href} {...other} />;
}
return (
<MuiLink component = {NextComposed} className = {className} ref = {innerRef} href = {href} {...other} />
);
}
Link.propTypes = {
activeClassName: PropTypes.string,
as: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
className: PropTypes.string,
href: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
naked: PropTypes.bool,
onClick: PropTypes.func,
prefetch: PropTypes.bool,
};
export default React.forwardRef((props, ref) => <Link {...props} innerRef = {ref} />);
Компонент Material UI Button уже имеет свойство href со строковым типом. (https://material-ui.com/api/button/). Я думаю, вы можете передать реквизит с другим именем.
Нравиться
<Button nextHref = {hrefAsObj} component = {CustomLink}>...</Button>
И
const CustomLink = ({ hrefAsObj }) => <NextLink href = {hrefAsObj}>...</NextLink>
Я открыл вопрос об этом в репозитории Material UI, PR будет объединен, чтобы предоставить решение.
// Link.js
/* eslint-disable jsx-a11y/anchor-has-content */
import React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { useRouter } from 'next/router';
import NextLink from 'next/link';
import MuiLink from '@material-ui/core/Link';
export const NextLinkComposed = React.forwardRef(function NextComposed(
props,
ref
) {
// eslint-disable-next-line react/prop-types
const {
href,
to,
as,
replace,
scroll,
passHref,
shallow,
prefetch,
...other
} = props;
return (
<NextLink href = {to} as = {as}>
<a ref = {ref} {...other} />
</NextLink>
);
});
NextLinkComposed.propTypes = {
as: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
prefetch: PropTypes.bool,
};
// A styled version of the Next.js Link component:
// https://nextjs.org/docs/#with-link
function Link(props) {
const {
href,
activeClassName = 'active',
className: classNameProps,
innerRef,
naked,
...other
} = props;
const router = useRouter();
const pathname = typeof href === 'string' ? href : href.pathname;
const className = clsx(classNameProps, {
[activeClassName]: router.pathname === pathname && activeClassName,
});
if (naked) {
return (
<NextLinkComposed
className = {className}
ref = {innerRef}
to = {href}
{...other}
/>
);
}
return (
<MuiLink
component = {NextLinkComposed}
className = {className}
ref = {innerRef}
to = {href}
{...other}
/>
);
}
Link.propTypes = {
activeClassName: PropTypes.string,
as: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
className: PropTypes.string,
href: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
naked: PropTypes.bool,
onClick: PropTypes.func,
prefetch: PropTypes.bool,
};
export default React.forwardRef((props, ref) => (
<Link {...props} innerRef = {ref} />
));
Файл Link.js теперь экспортирует NextLinkComposed кого можно использовать следующим образом:
// index.js
import React from "react";
import Button from "@material-ui/core/Button";
import { NextLinkComposed } from "../src/Link";
export default function Index() {
return (
<div>
<Button
component = {NextLinkComposed}
to = {{
pathname: "/about",
query: { name: "test" }
}}
variant = "contained"
>
Link button with url object
</Button>
</div>
);
}
Обратите внимание, что href prop переименовывается в to при использовании NextLinkComposed.