Envolviendo un enlace react-router en un botón html

Respuestas:

165

Si bien esto se mostrará en un navegador web, tenga en cuenta que:
⚠️ Anidar un html buttonen un html a(o viceversa) no es un html válido ⚠️. Si desea mantener su semántica html para los lectores de pantalla, utilice otro enfoque.

Envuelva al revés y obtendrá el botón original con el enlace adjunto. No se requieren cambios de CSS.

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

Aquí el botón es el botón HTML. También es aplicable a los componentes importados de bibliotecas de terceros como Semantic-UI-React .

 import { Button } from 'semantic-ui-react'
 ... 
 <Link to="/dashboard">
     <Button style={myStyle}>
        <p>Click Me!</p>
     </Button>
 </Link>
Naren Yellavula
fuente
1
Gracias, simple y funcional, mientras que la solución css de @ChaseJames no funciona. Quizás falta algo. Pero estoy usando bootstrap; import { Button } from 'react-bootstrap';.
Stefano Scarpanti
3
Al desplazarse por el documento, se desplazará hasta el ancla y luego al botón por separado. Para evitar esto, agregue tabindex = "- 1" al elemento Enlace. El enlace seguirá siendo seguido (al menos en Firefox ...) cuando el botón se active presionando enter.
temblor
4
Como sabe, Link crea una etiqueta de <a href="">ancla y una etiqueta de ancla no puede contener una etiqueta de botón.
taher
4
Si bien esto parece funcionar, es semánticamente incorrecto (e inválido en HTML 5). Consulte ¿Puedo anidar un elemento <button> dentro de un <a> usando HTML5?
imgx64
Si desactiva su botón, por ejemplo, hacer clic en el botón seguirá funcionando. Realmente no hay razón para usar esta solución hacky en comparación con lahistory.push opción
Burak
113

LinkButton componente - una solución para React Router v4

Primero, una nota sobre muchas otras respuestas a esta pregunta.

⚠️ Anidado <button>y <a>no es HTML válido. ⚠️

Cualquier respuesta aquí que sugiera anidar un html buttonen un Linkcomponente React Router (o viceversa) se representará en un navegador web, pero no es un HTML semántico, accesible o válido:

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

Haga clic para validar este marcado con validator.w3.org

Esto puede provocar problemas de diseño / estilo, ya que los botones no suelen colocarse dentro de los enlaces.


Usando una <button>etiqueta html con el <Link>componente React Router .

Si solo desea una buttonetiqueta html ...

<button>label text</button>

... entonces, esta es la forma correcta de obtener un botón que funcione como el Linkcomponente React Router ...

Use React Router's withRouter HOC para pasar estos accesorios a su componente:

  • history
  • location
  • match
  • staticContext

LinkButton componente

Aquí hay un LinkButtoncomponente para que lo copie / pasta :

// 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)

Luego importe el componente:

import LinkButton from '/components/LinkButton'

Utilice el componente:

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

Si necesita un método onClick:

<LinkButton
  to='/path/to/page'
  onClick={(event) => {
    console.log('custom event here!', event)
  }}
>Push My Buttons!</LinkButton>

Actualización: si está buscando otra opción divertida disponible después de que se escribió lo anterior, consulte este enlace useRouter .

Beau Smith
fuente
13
Esta debería ser la respuesta aceptada. Todas las demás respuestas son, en mi humilde opinión, hackeos y pueden proporcionar resultados inesperados. ¡Gracias!
Kyle
¿El uso del componente iría dentro del <div> .. </divelemento de App.js?
DJ2
@ DJ2 - Sí, o en cualquier otro componente en el que desee utilizarlo.
Beau Smith
¡Gracias por esto! En mi caso de uso particular, importé un botón material-ui import IconButton from '@material-ui/core/IconButton';y lo usé en lugar de <button />y funcionó muy bien.
Dan Evans
1
@Melounek - ¡Hola! Si el objetivo es tener una <a>etiqueta html diseñada visualmente para que parezca un "botón", entonces sí, la solución @ChaseJames lo logra. La pregunta anterior pregunta cómo usar un <button>elemento html, no un <a>.
Beau Smith
37

¿Por qué no decorar la etiqueta de enlace con el mismo CSS que un botón?

<Link 
 className="btn btn-pink"
 role="button"
 to="/"
 onClick={this.handleClick()}
> 
 Button1
</Link>
Chase James
fuente
Mantendré esto como último recurso principalmente porque tengo el CSS hecho para todos los botones que necesito, pero gracias por la sugerencia
Jose Rivera
Solución brillante. Para blueprint lo hago de esta manera: <Link className = {[Classes.BUTTON, Classes.MINIMAL, Classes.ICON + "-" + IconNames.PLUS] .join ("")} to = {"/ link"}> Enlace </Link>
caiiiycuk
13

Si está usando react-router-domy material-uipuede usar ...

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

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

Puedes leer más aquí .

Inam Ul Huq
fuente
Esta solución no funcionará con TS ya que la toprovisión Linkes obligatoria.
directorio
Esto funciona de maravilla, gracias
Pedro Silva
11

Puede usar useHistory hook desde react-router v5.1.0 .

El useHistorygancho le da acceso a la instancia del historial que puede usar para navegar.

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

export default function SomeComponent() {
  const { push } = useHistory()
  ...
  <button
    type="button"
    onClick={() => push('/some-link')}
  >
    Some link
  </button>
  ...
}
SandroMarques
fuente
¿Puedes presionar "algún enlace" a la función?
Rushino
En 2020 esto no me funciona. useHistory es nulo en mi versión de React
bikeman868
7

Yo uso Router y <Button />. No <Enlace />

<Button onClick={()=> {this.props.history.replace('/mypage')}}>
   HERE
</Button>
Alan
fuente
1
Creo que esta debería ser la solución predeterminada, pero tal vez se me hayan perdido algunos detalles. CUALQUIER componente / nodo / elemento que admita onClick={ () => navigateTo(somePath) }podría utilizar este enfoque. Ya sea que use redux y import {push} from 'connected-react-router'o simplemente history.push(o reemplace) como en su respuesta.
Thomas Fauskanger
6

⚠️ No, anidar un html buttonen un html a(o viceversa) no es un html válido

Raphael Rafatpanah
fuente
5
Entonces probé esto, y lo que obtuve fue un botón con un pequeño enlace en el que se puede hacer clic en el centro que cumplía su función. Sin embargo, hacer clic en cualquier otro lugar del botón pero el enlace directo no hizo nada.
Jose Rivera
Puede usar CSS para diseñar el botón y asegurarse de que tenga el tamaño correcto. Por ejemplo, estableciendo es widthy height.
Raphael Rafatpanah
2
Puede utilizar estilos en línea de la misma forma para la etiqueta de botón. O puede usar una de las muchas bibliotecas "css en JS". Yo prefiero styled-components. github.com/styled-components/styled-components
Raphael Rafatpanah
1
Funcionó con tu respuesta actualizada. Muchas gracias!
Jose Rivera
5
Esto no es correcto ... Deberías envolver el botón, no el enlace 😱
bensampaio
6

Para cualquiera que busque una solución con React 16.8+ (ganchos) y React Router 5:

Puedes cambiar la ruta usando un botón con el siguiente código:

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

React Router proporciona algunos accesorios a sus componentes, incluida la función push () en el historial que funciona de manera muy similar al elemento <Link to = 'path'>.

No es necesario que envuelva sus componentes con el Componente de orden superior "withRouter" para obtener acceso a esos accesorios.

pflevy
fuente
Esto funciona bien para mi; aunque no estoy seguro de qué pasa con esta solución que es específica para React 16.8+ o hooks. Creo que estás en buena forma con esto siempre que emplees BrowserRouter(basado en la API de historial HTML) en lugar de HashRouter.
pglezen
3

Con componentes de estilo, esto se puede lograr fácilmente

Primero diseñe un botón con estilo

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>
);

comprobar la casa del componente de estilo para más

Obed
fuente
3

Actualización para React Router versión 6:

Las diversas respuestas aquí son como una línea de tiempo de la evolución de react-router 🙂

Usando los últimos ganchos de react-router v6, esto ahora se puede hacer fácilmente con el useNavigategancho.

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

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

Muchas de las soluciones se han centrado en complicar las cosas.

Usar withRouter es una solución realmente larga para algo tan simple como un botón que se vincula a otra parte de la aplicación.

Si opta por SPA (aplicación de una sola página), la respuesta más fácil que he encontrado es usarla con el className equivalente del botón.

Esto asegura que mantiene el estado / contexto compartido sin recargar toda la aplicación como se hace con

import { NavLink } from 'react-router-dom'; // 14.6K (gzipped: 5.2 K)

// Where link.{something} is the imported data
<NavLink className={`bx--btn bx--btn--primary ${link.className}`} to={link.href} activeClassName={'active'}>
    {link.label}
</NavLink>

// Simplified version:
<NavLink className={'bx--btn bx--btn--primary'} to={'/myLocalPath'}>
    Button without using withRouter
</NavLink>
Hechos7Siete
fuente
Esto no funcionó para mí. Se pierde el estilo hove del botón v_v.
sbstnssndn
¿Quizás sus estilos de desplazamiento son demasiado restrictivos? ¿Están asignados a button.myClassName? Quite el botón del selector de estilo. O extenderlo en scss.
Acts7Seven