Emballage d'un lien react-router dans un bouton html

108

En utilisant la méthode suggérée: Voici le résultat: Un lien dans le bouton , Code entre les lignes de commentaire

Je me demandais s'il existe un moyen d'envelopper un Linkélément 'react-router'dans une buttonbalise HTML en utilisant react.

J'ai actuellement des Linkcomposants pour naviguer dans les pages de mon application, mais je voudrais mapper cette fonctionnalité à mes boutons HTML.

entrez la description de l'image ici entrez la description de l'image ici

José Rivera
la source

Réponses:

165

Bien que cela s'affiche dans un navigateur Web,
sachez que: ⚠️ L'buttona imbrication d' un html dans un html (ou vice-versa) n'est pas un html valide ⚠️. Si vous souhaitez conserver votre sémantique html aux lecteurs d'écran, utilisez une autre approche.

Faites l'emballage dans le sens inverse et vous obtenez le bouton d'origine avec le lien attaché. Aucune modification CSS requise.

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

Ici, le bouton est le bouton HTML. Il est également applicable aux composants importés de bibliothèques tierces comme Semantic-UI-React .

 import { Button } from 'semantic-ui-react'
 ... 
 <Link to="/dashboard">
     <Button style={myStyle}>
        <p>Click Me!</p>
     </Button>
 </Link>
Naren Yellavula
la source
1
Merci, simple et fonctionnel, alors que la solution css de @ChaseJames ne fonctionne pas. Il manque peut-être quelque chose. Mais j'utilise bootstrap; import { Button } from 'react-bootstrap';.
Stefano Scarpanti
3
Lors de la tabulation dans le document, vous tabulez jusqu'à l'ancre, puis sur le bouton séparément. Pour éviter cela, ajoutez tabindex = "- 1" à l'élément Link. Le lien sera toujours suivi (au moins dans Firefox ...) lorsque le bouton est activé en appuyant sur Entrée.
tremby
4
Comme vous le savez, Link crée une balise d' <a href="">ancrage et la balise d'ancrage ne peut pas contenir de balise de bouton.
taher
4
Bien que cela semble fonctionner, c'est sémantiquement faux (et invalide en HTML 5). Voir Puis-je imbriquer un élément <button> dans un <a> en utilisant HTML5?
imgx64
Si vous désactivez votre bouton, par exemple, cliquer sur le bouton fonctionnera toujours. Il n'y a vraiment aucune raison d'utiliser cette solution hacky par rapport à l' history.pushoption
Burak
113

LinkButton composant - une solution pour React Router v4

Tout d'abord, une note sur de nombreuses autres réponses à cette question.

⚠️ Nesting <button>et <a>n'est pas valide html. ⚠️

Toute réponse ici suggérant d'imbriquer un html buttondans un Linkcomposant React Router (ou vice-versa) sera rendue dans un navigateur Web, mais ce n'est pas un html sémantique, accessible ou valide:

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

Cliquez pour valider ce balisage avec validator.w3.org

Cela peut entraîner des problèmes de mise en page / de style, car les boutons ne sont généralement pas placés à l'intérieur des liens.


Utilisation d'une <button>balise html avec le <Link>composant React Router .

Si vous voulez uniquement une buttonbalise HTML …

<button>label text</button>

… Alors, voici la bonne façon d'obtenir un bouton qui fonctionne comme le Linkcomposant de React Router …

Utilisez le withRouter HOC de React Router pour transmettre ces accessoires à votre composant:

  • history
  • location
  • match
  • staticContext

LinkButton composant

Voici un LinkButtoncomposant à copier / pâtes :

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

Importez ensuite le composant:

import LinkButton from '/components/LinkButton'

Utilisez le composant:

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

Si vous avez besoin d'une méthode onClick:

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

Mise à jour: Si vous recherchez une autre option amusante disponible après l'écriture de ce qui précède , consultez ce hook useRouter .

Beau Smith
la source
13
Cela devrait être la réponse acceptée. Toutes les autres réponses sont, à mon humble avis, hacks et peuvent fournir des résultats inattendus. Merci!
Kyle
L'utilisation du composant irait-elle à l'intérieur de l' <div> .. </divélément de App.js?
DJ2
@ DJ2 - Oui, ou dans tout autre composant dans lequel vous souhaitez l'utiliser.
Beau Smith
Merci pour cela! Dans mon cas d'utilisation particulier, j'ai importé un bouton material-ui avec import IconButton from '@material-ui/core/IconButton';et je l'ai utilisé à la place de <button />et cela a très bien fonctionné.
Dan Evans
1
@Melounek - Bonjour! Si le but est d'avoir une <a>balise html visuellement stylisée pour ressembler à un "bouton", alors oui, la solution @ChaseJames y parvient. La question ci-dessus demande comment utiliser un <button>élément html, pas un <a>.
Beau Smith
37

Pourquoi ne pas simplement décorer la balise de lien avec le même CSS qu'un bouton.

<Link 
 className="btn btn-pink"
 role="button"
 to="/"
 onClick={this.handleClick()}
> 
 Button1
</Link>
Chase James
la source
Je vais garder cela en dernier recours principalement parce que j'ai le CSS fait pour tous les boutons dont j'ai besoin mais merci pour la suggestion
Jose Rivera
Solution brillante. Pour le plan, je fais de cette manière: <Link className = {[Classes.BUTTON, Classes.MINIMAL, Classes.ICON + "-" + IconNames.PLUS] .join ("")} to = {"/ link"}> Lien </Link>
caiiiycuk
13

Si vous utilisez react-router-domet que material-uivous pouvez utiliser ...

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

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

Vous pouvez en savoir plus ici .

Inam Ul Huq
la source
Cette solution ne fonctionnera pas avec TS car toprop on Linkest obligatoire.
annuaire
Cela fonctionne comme un charme, merci
Pedro Silva
11

Vous pouvez utiliser useHistory hook depuis react-router v5.1.0 .

Le useHistoryhook vous donne accès à l'instance d'historique que vous pouvez utiliser pour naviguer.

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
la source
Pouvez-vous pousser "un lien" vers la fonction?
Rushino le
En 2020, cela ne fonctionne pas pour moi. useHistory est nul dans ma version de React
bikeman868
7

J'utilise Router et <Button />. Non <Link />

<Button onClick={()=> {this.props.history.replace('/mypage')}}>
   HERE
</Button>
Alan
la source
1
Je pense que cela devrait être la solution par défaut, mais peut-être que certains détails me sont perdus? TOUT composant / nœud / élément qui prend en charge onClick={ () => navigateTo(somePath) }pourrait utiliser cette approche. Que ce soit en utilisant redux et import {push} from 'connected-react-router'ou simplement history.push(ou remplacer) comme dans votre réponse.
Thomas Fauskanger
6

⚠️ Non, l' imbrication d'un html buttondans un html a(ou vice-versa) n'est pas un html valide

Raphael Rafatpanah
la source
5
J'ai donc essayé ceci, et j'ai obtenu un bouton avec un petit lien cliquable au centre qui remplissait sa fonction. Cependant, cliquer n'importe où ailleurs sur le bouton mais le lien direct n'a rien fait.
Jose Rivera
Vous pouvez utiliser CSS pour styliser le bouton afin de vous assurer qu'il est correctement dimensionné. Par exemple, en définissant c'est widthet height.
Raphael Rafatpanah
2
Vous pouvez utiliser les styles en ligne de la même manière pour la balise de bouton. Ou vous pouvez utiliser l'une des nombreuses bibliothèques "css in JS". Je préfère styled-components. github.com/styled-components/styled-components
Raphael Rafatpanah
1
Cela a fonctionné avec votre réponse mise à jour. Merci beaucoup!
Jose Rivera
5
Ce n'est pas correct ... Vous devriez envelopper le bouton, pas le lien 😱
bensampaio
6

Pour tous ceux qui recherchent une solution utilisant React 16.8+ (hooks) et React Router 5:

Vous pouvez modifier l'itinéraire à l'aide d'un bouton avec le code suivant:

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

React Router fournit quelques accessoires à vos composants, y compris la fonction push () sur l' historique qui fonctionne à peu près comme l'élément <Link to = 'path'>.

Vous n'avez pas besoin d'encapsuler vos composants avec le composant d'ordre supérieur "withRouter" pour accéder à ces accessoires.

pflevy
la source
Cela fonctionne bien pour moi; bien que je ne sache pas ce que cette solution est spécifique à React 16.8+ ou aux hooks. Je pense que vous êtes en bonne forme avec cela tant que vous utilisez BrowserRouter(basé sur l'API de l'historique HTML) au lieu de HashRouter.
pglezen le
3

Avec des composants stylisés, cela peut être facilement réalisé

Commencez par concevoir un bouton stylé

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

consultez la maison du composant stylisé pour en savoir plus

Obed
la source
3

Mise à jour pour React Router version 6:

Les différentes réponses ici sont comme une chronologie de l'évolution de React-Router 🙂

En utilisant les derniers hooks de react-router v6, cela peut maintenant être fait facilement avec le useNavigatehook.

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

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

Bon nombre des solutions se sont concentrées sur la complication des choses.

Utiliser withRouter est une solution très longue pour quelque chose d'aussi simple qu'un bouton qui renvoie à un autre endroit de l'application.

Si vous optez pour SPA (application d'une seule page), la réponse la plus simple que j'ai trouvée est de l'utiliser avec le nom de classe équivalent du bouton.

Cela garantit que vous maintenez l'état / le contexte partagé sans recharger l'intégralité de votre application, comme c'est le cas avec

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>
Actes 7Sept
la source
Cela n'a pas fonctionné pour moi. Le style hove du bouton est perdu v_v.
sbstnssndn
Peut-être que vos styles de survol sont trop restrictifs? Sont-ils affectés à button.myClassName? Supprimer le bouton du sélecteur de style. Ou étendez-le en scss.
Acts7Seven