Using Button as an Link is a common scenario. In previous version( 0.9.*), Button Component use button tag by default with an htmlType prop, which is fine when I used React-router Link (which is an anchor) to wrap Button.
After updating to 1.0.0, The Button Component use anchor tag , I get warnings about anchor nested in anchor
I shall say this is not a bug and I can replace React-router Link with router methods to get rid of
those warnings.
I'm just wondering on what kind of considerations that you changed the button component to use anchor instead of button.
fisrt, why you did anchor nested in anchor ? Are you put a <Link> in <Button> ? That is unnecessary
why use a rather than button, possibly because of this
Before the update, I use button like this
<Link><Button>...</Button><Link>
which results in
<a><button>...</button></a>
because many button-like elements are actually links and I did't have to add extra page jumping event handlers to them.
After the update, those warnings showed up.
consider change Button inner element to div in next release.
cc @silentcloud @warmhug
@yup9
Sorry, i decided not to change this, since i looked at many other open source ui components,
use <a> for a Button is a normal way, which avoid native button's ugly style and still keep good Accessible Rich Internet Applications (ARIA) . ref https://github.com/ant-design/ant-design-mobile/pull/897
I suggest you use onClick callback to do router change to avoid the warning.
going to close this, if you have any further question, feel free to repoen :)
I'm ok with this. Thanks for your time.😄
Why not just giving the <Link> the needed className?
<Link className="ant-btn ant-btn-primary" to="/yoururl">Your Text</Link>
@kayyyy that relies on implementation details (classnames) which is not great. Also, you're no longer using the Button component, so you lose out on all the benefits (eg: disabled button, loading status, icon...etc).
The Button component should really allow you to override the underlying component, eg:
import { Link } from 'react-router-dom';
import { Button } from 'antd';
<Button component={Link} to="/my-link">Link</Button>
This is a popular approach among react ui libraries, for example material ui:
https://material-ui.com/demos/buttons/#third-party-routing-library
Hello guys, I definitely agree with @VictorChen. It looks to me the soundest way to go.
Meanwhile I use this ugly workaround when it comes to react-router:
import React from 'react'
import PropTypes from 'prop-types'
import { withRouter } from 'react-router-dom'
import Button from 'antd-mobile/lib/button'
import 'antd-mobile/lib/button/style/css'
const ButtonAntd = ({
linkTo,
linkMode,
match,
location,
history,
staticContext,
...props
}) =>
linkTo
? (
<Button
{...props}
onClick={() => {
if (linkMode === 'replace') {
history.replace(linkTo)
} else {
history.push(linkTo)
}
}}
/>
)
: <Button {...props} />
ButtonAntd.propTypes = {
linkTo: PropTypes.string,
linkMode: PropTypes.oneOf(['replace']),
location: PropTypes.object.isRequired,
match: PropTypes.object.isRequired,
history: PropTypes.object.isRequired,
staticContext: PropTypes.object,
}
ButtonAntd.defaultProps = {
linkTo: null,
linkMode: null,
staticContext: null,
}
export default withRouter(ButtonAntd)
Hello any updates on this? It is such a simple update, to allow pass tag prop. It would solve lots of issues. All other libraries has it
I've written simple wrapper for Button. Thought this might help you all until we have some more flexibility on Buttons underying tag. This solution also supports opening link in new tab 🙂
import Button, { ButtonProps } from "antd/lib/button";
import { createLocation, Location, LocationDescriptor } from "history";
import React from "react";
import { __RouterContext as RouterContext } from "react-router";
import invariant from "tiny-invariant";
const normalizeToLocation = (to: LocationDescriptor, currentLocation: Location) => {
return typeof to === "string" ? createLocation(to, null, undefined, currentLocation) : to;
};
function isModifiedEvent(event: any) {
return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
}
type LinkButtonProps = ({ to: LocationDescriptor; toExternal?: undefined } | { to?: undefined; toExternal: string }) & {
replace?: boolean;
"data-test-id"?: string;
} & ButtonProps;
const LinkButton: React.FunctionComponent<LinkButtonProps> = ({
"data-test-id": dataTestId,
replace,
to,
onClick,
toExternal,
...rest
}) => {
return (
<RouterContext.Consumer>
{context => {
invariant(context, "You should not use <ButtonLink> outside a <Router>");
const { history } = context;
const [href, navigate] = ((): [string, (() => void) | undefined] => {
if (toExternal) {
return [toExternal, undefined];
}
if (to) {
const location = normalizeToLocation(to, context.location);
const navigate = () => {
const method: (location: LocationDescriptor) => void = replace
? history.replace
: history.push;
method(to);
};
return [location ? history.createHref(location) : "", navigate];
}
throw new Error("Either 'to' or 'toExternal' need to be defined");
})();
const { target } = rest;
return (
<Button
data-test-id={dataTestId}
{...rest}
href={href}
onClick={
navigate &&
(event => {
try {
onClick && onClick(event);
} catch (ex) {
event.preventDefault();
throw ex;
}
if (
!event.defaultPrevented &&
event.button === 0 &&
(!target || target === "_self") &&
!isModifiedEvent(event)
) {
event.preventDefault();
navigate();
}
})
}
/>
);
}}
</RouterContext.Consumer>
);
};
export default LinkButton;
This is basically copy-paste from https://github.com/ReactTraining/react-router/blob/b529499efcb906c814b9a3a68e2b4292a15b09c8/packages/react-router-dom/modules/Link.js
Most helpful comment
@kayyyy that relies on implementation details (classnames) which is not great. Also, you're no longer using the
Buttoncomponent, so you lose out on all the benefits (eg: disabled button, loading status, icon...etc).The
Buttoncomponent should really allow you to override the underlying component, eg:This is a popular approach among react ui libraries, for example material ui:
https://material-ui.com/demos/buttons/#third-party-routing-library