在HTML按钮中包装一个React-Router链接

177

使用建议的方法: 这是结果: 按钮内的链接, 注释行之间的代码

我想知道是否有一种方法可以在HTML中使用React将'react-router'中的元素包裹在

19个回答

274

尽管这在网络浏览器中可以呈现,但要注意:
⚠️嵌套一个 HTML button 在一个 HTML a 内部(或反之)不是有效的 HTML ⚠️。 如果您想保持对屏幕阅读器的HTML语义,请使用其他方法。

以相反的方式包装即可获得带有链接的原始按钮,无需更改CSS。

 <Link to="/dashboard">
     <button type="button">
          Click Me!
     </button>
 </Link>

这里的按钮是HTML按钮。它也适用于从第三方库(如Semantic-UI-React)导入的组件。

 import { Button } from 'semantic-ui-react'
 ... 
 <Link to="/dashboard">
     <Button style={myStyle}>
        <p>Click Me!</p>
     </Button>
 </Link>

6
当您通过文档进行Tab键切换时,将先定位到锚点,然后再定位到按钮。为了避免这种情况,请在链接元素中添加tabindex="-1"。当通过按Enter键激活按钮时,链接仍将被访问(至少在Firefox浏览器中是如此)。 - tremby
8
正如您所知,Link 创建锚标签 <a href="">,而锚标签不能包含按钮标签。 - taher
6
虽然这样似乎可行,但从语义上来说是错误的(并且在 HTML 5 中是无效的)。请参见Can I nest a <button> element inside an <a> using HTML5? - imgx64
2
如果您使按钮不可用,例如,点击该按钮仍将起作用。与“history.push”选项相比,使用这种hacky解决方案的真正原因是没有理由的。 - Burak
1
semantic-ui-react现在支持组件增强,您需要在常规的semantic-ui-react按钮组件上使用as={Link}属性。使用您在Link组件中使用的原始props。 - David Clews
显示剩余3条评论

151

LinkButton 组件 - 适用于 React Router v4 的解决方案

首先,关于此问题的许多其他回答需要注意。

⚠️ 嵌套 <button><a> 不是有效的 HTML ⚠️

任何在这里建议将 html button 嵌套在 React Router 的 Link 组件中(或反之亦然)的答案都会在 web 浏览器中呈现,但它并不是语义化、可访问或有效的 HTML:

<a stuff-here><button>label text</button></a>
<button><a stuff-here>label text</a></button>

点击此处,使用 validator.w3.org 验证此标记

由于按钮通常不会放置在链接内部,因此这可能会导致布局和样式问题。


在 React Router 的 <Link> 组件中使用 HTML 的 <button> 标签。

如果你只想要一个 HTML 的 button 标签...

<button>label text</button>

如果您想要一个像React Router的组件一样工作的按钮,那么这里有正确的方法...

使用React Router的withRouter高阶组件将以下属性传递给您的组件:

  • history
  • location
  • match
  • staticContext

LinkButton组件

这是一个LinkButton组件,供您复制/粘贴:

// file: /components/LinkButton.jsx
import React from 'react'
import PropTypes from 'prop-types'
import { withRouter } from 'react-router'

const LinkButton = (props) => {
  const {
    history,
    location,
    match,
    staticContext,
    to,
    onClick,
    // ⬆ filtering out props that `button` doesn’t know what to do with.
    ...rest
  } = props
  return (
    <button
      {...rest} // `children` is just another prop!
      onClick={(event) => {
        onClick && onClick(event)
        history.push(to)
      }}
    />
  )
}

LinkButton.propTypes = {
  to: PropTypes.string.isRequired,
  children: PropTypes.node.isRequired
}

export default withRouter(LinkButton)

然后导入组件:
import LinkButton from '/components/LinkButton'

使用该组件:

<LinkButton to='/path/to/page'>Push My Buttons!</LinkButton>

如果您需要一个“onClick”方法:
<LinkButton
  to='/path/to/page'
  onClick={(event) => {
    console.log('custom event here!', event)
  }}
>Push My Buttons!</LinkButton>

更新:如果您正在寻找上述内容发布后推出的另一个有趣选项,请查看此useRouter钩子


28
这应该是被采纳的答案。我认为,所有其他答案都是hack,并且可能会产生意想不到的结果。谢谢! - Kyle
谢谢!在我的特定用例中,我使用了 import IconButton from '@material-ui/core/IconButton'; 导入了一个 material-ui 按钮,并使用它代替 <button />,效果非常好。 - Daniel Jonce Evans
2
但是在这里你不能使用像Ctrl + Click或在点击之前在页脚中看到目标URL等<a>功能 :( 我投票支持@ChaseJames的解决方案,因为它最干净。 - Melounek
1
@Melounek - 你好!如果目标是让<a> HTML 标签在视觉上呈现为“按钮”样式,那么@ChaseJames 的解决方案可以实现这一目标。上面的问题问的是如何使用<button> HTML 元素,而不是<a> - Beau Smith
现在,在版本6的react-router-dom中不再提供withRouter,那些使用最新版本的人可以在函数组件中使用hooks进行操作。详情请参见:https://dev59.com/MFgQ5IYBdhLWcg3wolao#70033584 - Sabaoon Bedar
显示剩余4条评论

64

为什么不直接用与按钮相同的CSS装饰链接标签呢?

<Link 
 className="btn btn-pink"
 role="button"
 to="/"
 onClick={this.handleClick()}
> 
 Button1
</Link>

1
我会把这个作为最后的选择,因为我已经完成了我所需按钮的CSS,但还是感谢你的建议。 - Jose Rivera
1
太棒了,对于蓝图,我是这样做的:<Link className={[Classes.BUTTON, Classes.MINIMAL, Classes.ICON + "-" + IconNames.PLUS].join(" ")} to={"/link"}>链接</Link>。 - caiiiycuk
3
您不能在虚拟按钮上使用启用/禁用状态。 - maco
1
这可能是最佳方法,因为导航通常应使用链接语义,即 <a/> 标签。但应该注意的是,仅将针对 <button/> 设计的现有 CSS 类应用于 <a/> 可能会导致样式失效。原因在于这些元素在 CSS 行为方面根本不同,请参见 此问题 。在我的情况下,我需要先重构样式以应用此解决方案。 - bluenote10

44

如果您正在使用react-router-dommaterial-ui,您可以使用...

import { Link } from 'react-router-dom'
import Button from '@material-ui/core/Button';

<Button component={Link} to="/open-collective">
  Link
</Button>

你可以在这里阅读更多内容。


这个解决方案在TS中不起作用,因为Link上的to属性是必需的。 - directory
1
我的错误是从 @material-ui/core 导入 Link,而不是从 react-router-dom 导入。 - GuyB
1
这在TS中对我起作用了 - 没有类型错误。 - JBS

30

自从react-router v5.1.0以来,您可以使用useHistory钩子

useHistory钩子使您能够访问历史实例,您可以使用它进行导航。

import React from 'react'
import { useHistory } from 'react-router-dom'

export default function SomeComponent() {
  const { push } = useHistory()
  ...
  <button
    type="button"
    onClick={() => push('/some-link')}
  >
    Some link
  </button>
  ...
}

注意:请注意这种方法可以回答问题,但像@ziz194在评论中提到的那样,不可访问

尽管如此,这种方法无法访问,因为按钮不会成为一个标签,因此它没有链接行为,比如在新页面中打开链接。对于屏幕阅读器也不是最优选择。

结论:更好的处理方法是使用标签并进行样式设置。


导入应该是'react-router-dom'而不是'react-router'。 - Rafael
嘿@Rafael,谢谢。我把它改成了 react-router-dom - SandroMarques
1
然而,这是不可访问的,因为该按钮不会成为<a>标签,因此它没有链接行为,例如在新页面中打开链接。对于屏幕阅读器来说也不是最佳选择。 - ziz194
1
@ziz194,我刚刚在我的答案中添加了你的评论。感谢你增加了价值。 - SandroMarques
2
在 react-router-dom 版本 6 中,您无法使用 useHistory,而应该使用 useNavigate。 - Sabaoon Bedar
显示剩余3条评论

19

React Router版本6更新:

这里的各种回答就像react-router演进的时间线一样。

使用react-router v6最新的hooks,可以轻松地使用useNavigate hook实现此操作。

import { useNavigate } from 'react-router-dom'      

function MyLinkButton() {
  const navigate = useNavigate()
  return (
      <button onClick={() => navigate("/home")}>
        Go Home
      </button>
  );
}

4
目前版本6还未发布,因此该回答令人困惑。截至本评论,最新版本为5.2.0。 - davidawad
版本6目前在beta版中可用,并在一些项目中使用。请在此处查看版本选项卡:https://www.npmjs.com/package/react-router - J M Rossy
使用按钮进行导航在语义上是不正确的,<a>标签用于导航,而不是<button>标签。 - undefined

9
使用 styled components 能够轻松实现这个目标。
首先设计一个带样式的按钮。
import styled from "styled-components";
import {Link} from "react-router-dom";

const Button = styled.button`
  background: white;
  color:red;
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid red;
  border-radius: 3px;
`
render(
    <Button as={Link} to="/home"> Text Goes Here </Button>
);

请访问styled-components主页获取更多信息。


7

我使用Router和<Button/>,没有<Link/>

<Button onClick={()=> {this.props.history.replace('/mypage')}}>
   HERE
</Button>

2
我认为这应该是默认解决方案,但也许有些细节我不清楚?任何支持 onClick={ () => navigateTo(somePath) } 的组件/节点/元素都可以使用这种方法。无论是使用redux和 import {push} from 'connected-react-router' 还是像你的答案中一样只是使用 history.push(或replace)。 - Thomas Fauskanger
此解决方案无法访问,您不能使用此解决方案在新页面中打开链接。 - ziz194

6

5
我尝试了这个,结果得到的是一个带有可点击链接的小按钮,并能够起到它的功能。但是如果在除了直接链接以外的任何地方点击按钮,都不会有任何反应。 - Jose Rivera
当然。已上传到页面顶部。 - Jose Rivera
2
你可以使用内联样式同样的方法为按钮标签添加样式。或者你可以使用众多的“CSS in JS”库之一。我更喜欢styled-components。https://github.com/styled-components/styled-components - Raphael Rafatpanah
1
它与您更新的答案一起工作了。非常感谢! - Jose Rivera
5
这不是正确的做法... 你应该包装按钮,而不是链接。 - bensampaio
显示剩余10条评论

6

对于任何想使用React 16.8+ (hooks)和React Router 5的人:

你可以使用以下代码通过按钮更改路由:

<button onClick={() => props.history.push("path")}>

React Router提供了一些属性给你的组件,其中包括history上的push()函数,它的用法与 <Link to='path'> 元素类似。

你不需要使用高阶组件 "withRouter" 包裹你的组件来获得这些属性的访问权限。


这对我来说很好用,虽然我不确定这个解决方案与React 16.8+或hooks有什么特定关系。只要使用BrowserRouter(基于HTML历史记录API)而不是HashRouter,你就可以放心使用它了。 - pglezen
非链接元素的点击导航效果一般。您无法像链接一样进行交互,因此无法使用“在新标签页中打开”等功能。 - zcharles

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接