¿Cómo invocar manualmente Link en React-router?

9 minutos de lectura

Avatar de usuario de Alan Souza
alan souza

Tengo un componente que recibe a través de props un <Link/> objeto de enrutador de reacción. Cada vez que el usuario hace clic en el botón ‘siguiente’ dentro de este componente, quiero invocar <Link/> objeto manualmente.

En este momento, estoy usando árbitros para acceder a la instancia de respaldo y haciendo clic manualmente en la etiqueta ‘a’ que <Link/> genera

Pregunta: ¿Hay alguna manera de invocar manualmente el enlace (por ejemplo, this.props.next.go)?

Este es el código actual que tengo:

//in MasterPage.js
var sampleLink = <Link to="/sample">Go To Sample</Link>
<Document next={sampleLink} />

//in Document.js
...
var Document = React.createClass({
   _onClickNext: function() {
      var next = this.refs.next.getDOMNode();
      next.querySelectorAll('a').item(0).click(); //this sounds like hack to me
   },
   render: function() {
      return (
         ...
         <div ref="next">{this.props.next} <img src="rightArrow.png" onClick={this._onClickNext}/></div>
         ...
      );
   }
});
...

Este es el código que me gustaría tener:

//in MasterPage.js
var sampleLink = <Link to="/sample">Go To Sample</Link>
<Document next={sampleLink} />

//in Document.js
...
var Document = React.createClass({
   render: function() {
      return (
         ...
         <div onClick={this.props.next.go}>{this.props.next.label} <img src="rightArrow.png" /> </div>
         ...
      );
   }
});
...

Avatar de usuario de Matt Lo
mate lo

React Router v6 – React 17+ (actualizado el 14/01/2022)

import React, {useCallback} from 'react';
import {useNavigate} from 'react-router-dom';

export default function StackOverflowExample() {
  const navigate = useNavigate();
  const handleOnClick = useCallback(() => navigate('/sample', {replace: true}), [navigate]);

  return (
    <button type="button" onClick={handleOnClick}>
      Go home
    </button>
  );
}

Nota: Para esta respuesta, el cambio principal entre v6 y v5 es useNavigate es ahora el gancho React preferido. useHistory está en desuso y no se recomienda.

React Router v5 – React 16.8+ con ganchos

Si estás aprovechando Ganchos de reacciónpuedes aprovechar las useHistory API que proviene de React Router v5.

import React, {useCallback} from 'react';
import {useHistory} from 'react-router-dom';

export default function StackOverflowExample() {
  const history = useHistory();
  const handleOnClick = useCallback(() => history.push('/sample'), [history]);

  return (
    <button type="button" onClick={handleOnClick}>
      Go home
    </button>
  );
}

Otra forma de escribir el controlador de clics si no desea utilizar useCallback

const handleOnClick = () => history.push('/sample');

React Router v4 – Componente de redirección

La forma recomendada de v4 es permitir que su método de procesamiento atrape una redirección. Utilice el estado o los accesorios para determinar si es necesario mostrar el componente de redirección (lo que luego activará una redirección).

import { Redirect } from 'react-router';

// ... your class implementation

handleOnClick = () => {
  // some action...
  // then redirect
  this.setState({redirect: true});
}

render() {
  if (this.state.redirect) {
    return <Redirect push to="/sample" />;
  }

  return <button onClick={this.handleOnClick} type="button">Button</button>;
}

Referencia: https://reacttraining.com/react-router/web/api/Redirect

React Router v4 – Contexto del enrutador de referencia

También puedes aprovechar RouterEl contexto que está expuesto al componente React.

static contextTypes = {
  router: PropTypes.shape({
    history: PropTypes.shape({
      push: PropTypes.func.isRequired,
      replace: PropTypes.func.isRequired
    }).isRequired,
    staticContext: PropTypes.object
  }).isRequired
};

handleOnClick = () => {
  this.context.router.push('/sample');
}

Así es como <Redirect /> funciona bajo el capó.

Referencia: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/Redirect.js#L46,L60

React Router v4 – Objeto de historial de mutación externa

Si aún necesita hacer algo similar a la implementación de v2, puede crear una copia de BrowserRouter luego exponer el history como una constante exportable. A continuación se muestra un ejemplo básico, pero puede componerlo para inyectarlo con accesorios personalizables si es necesario. Hay advertencias señaladas con los ciclos de vida, pero siempre debe volver a renderizar el enrutador, al igual que en v2. Esto puede ser útil para los redireccionamientos después de una solicitud de API desde una función de acción.

// browser router file...
import createHistory from 'history/createBrowserHistory';
import { Router } from 'react-router';

export const history = createHistory();

export default class BrowserRouter extends Component {
  render() {
    return <Router history={history} children={this.props.children} />
  }
}

// your main file...
import BrowserRouter from './relative/path/to/BrowserRouter';
import { render } from 'react-dom';

render(
  <BrowserRouter>
    <App/>
  </BrowserRouter>
);

// some file... where you don't have React instance references
import { history } from './relative/path/to/BrowserRouter';

history.push('/sample');

Último BrowserRouter extender: https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/modules/BrowserRouter.js

Reaccionar enrutador v2

Empuje un nuevo estado a la browserHistory instancia:

import {browserHistory} from 'react-router';
// ...
browserHistory.push('/sample');

Referencia: https://github.com/reactjs/react-router/blob/master/docs/guides/NavigatingOutsideOfComponents.md

  • hashHistory.push(‘/muestra’); si está utilizando hashHistory en lugar de browserHistory

    – sanath_p

    21 dic 2016 a las 22:40

  • esto es especialmente útil en la biblioteca material-ui ya que usar containerElement={} no siempre invoca el enlace

    – Vishal Disawar

    28 de enero de 2017 a las 6:07

  • Tenga en cuenta que con la opción de redireccionamiento debe especificar push (es decir, ). De forma predeterminada, realizará un reemplazo que no es lo mismo que invocar manualmente un enlace

    – aw04

    3 mayo 2017 a las 15:28


  • @jokab puede usar en lugar de github.com/ReactTraining/react-router/blob/master/packages/…

    – Matt Lo

    19 de septiembre de 2017 a las 3:57

  • Redirigir no funciona para mí, pero la solución aw04 con withRouter es más simple y funciona

    – stackdave

    10 de junio de 2018 a las 5:35

avatar de usuario de aw04
aw04

React Router 4 incluye un con enrutador HOC que le da acceso a la history objeto a través de this.props:

import React, {Component} from 'react'
import {withRouter} from 'react-router-dom'

class Foo extends Component {
  constructor(props) {
    super(props)

    this.goHome = this.goHome.bind(this)
  }

  goHome() {
    this.props.history.push("https://stackoverflow.com/")
  }

  render() {
    <div className="foo">
      <button onClick={this.goHome} />
    </div>
  }
}

export default withRouter(Foo)

  • Esto funcionó para mí y parece la solución más simple.

    – Rubycut

    27 de junio de 2017 a las 12:29

  • Esta es la mejor solución. No entiendo por qué tiene tan pocos votos.

    – Benoit

    5 de noviembre de 2017 a las 21:50

  • sí, puede hacer clic en el enlace varias veces y la función de retroceso del navegador no funcionará. tendrá que hacer clic en el navegador atrás varias veces para volver realmente

    – Vladyslav Tereshin

    4 de marzo de 2019 a las 10:42

  • @VladyslavTereshyn puede agregar algo de lógica condicional: if ((this.props.location.pathname + this.props.location.search) !== browserToPath) { … }

    – MattWeiler

    20 de abril de 2020 a las 14:16


  • después de buscar durante una hora, esta solución finalmente funcionó. ¡Gracias!

    – Hombre iónico

    17 de agosto de 2021 a las 15:47

Avatar de usuario de Paulo Mateus
paulo mateus

En el versión 5.xpuedes usar useHistory gancho de react-router-dom:

// Sample extracted from https://reacttraining.com/react-router/core/api/Hooks/usehistory
import { useHistory } from "react-router-dom";

function HomeButton() {
  const history = useHistory();

  function handleClick() {
    history.push("/home");
  }

  return (
    <button type="button" onClick={handleClick}>
      Go home
    </button>
  );
}

  • Esta es la mejor solución. Si agrega algo de lógica condicional, puede evitar entradas duplicadas en el historial cuando un usuario hace clic en el mismo botón varias veces: if ((routerHistory.location.pathname + routerHistory.location.search) !== navigateToPath) { routerHistory.push(navigateToPath); }

    – MattWeiler

    20 de abril de 2020 a las 15:37

  • necesitaba declarar history variable, directorio invocando useHistory().push no está permitido por las reglas de ganchos

    – onmyway133

    2 jun 2020 a las 16:00

  • esta parece la solución reactiva más moderna.

    – Young Jae

    30 de julio de 2020 a las 9:50

  • Esto está bien 👌 y fuera de curso funciona para v5.x Con las funciones de flecha, uno podría simplemente avanzar como onClick={ () => history.push('/home') }

    – KeshavDulal

    23 de septiembre de 2020 a las 6:37


avatar de usuario de grechut
grechut

https://github.com/rackt/react-router/blob/bf89168acb30b6dc9b0244360bcbac5081cf6b38/examples/transitions/app.js#L50

o incluso puede intentar ejecutar onClick this (solución más violenta):

window.location.assign("/sample");

Las respuestas aquí están desactualizadas.

Reaccionar enrutador 6

useHistory está en desuso v6 usa el useNavigate gancho en su lugar.

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

const navigate = useNavigate()

navigate(`/somewhere`, { replace: true })

Ok, creo que pude encontrar una solución adecuada para eso.

Ahora, en lugar de enviar <Link/> como apuntalar a Documento, envío <NextLink/> que es un contenedor personalizado para el enlace del enrutador de reacción. Al hacer eso, puedo tener la flecha hacia la derecha como parte de la estructura del enlace y al mismo tiempo evito tener un código de enrutamiento dentro del objeto Documento.

El código actualizado se ve así:

//in NextLink.js
var React = require('react');
var Right = require('./Right');

var NextLink = React.createClass({
    propTypes: {
        link: React.PropTypes.node.isRequired
    },

    contextTypes: {
        transitionTo: React.PropTypes.func.isRequired
    },

    _onClickRight: function() {
        this.context.transitionTo(this.props.link.props.to);
    },

    render: function() {
        return (
            <div>
                {this.props.link}
                <Right onClick={this._onClickRight} />
            </div>  
        );
    }
});

module.exports = NextLink;

...
//in MasterPage.js
var sampleLink = <Link to="/sample">Go To Sample</Link>
var nextLink = <NextLink link={sampleLink} />
<Document next={nextLink} />

//in Document.js
...
var Document = React.createClass({
   render: function() {
      return (
         ...
         <div>{this.props.next}</div>
         ...
      );
   }
});
...

PD: Si está utilizando la última versión de react-router, es posible que deba usar this.context.router.transitionTo en lugar de this.context.transitionTo. Este código funcionará bien para la versión 0.12.X de react-router.

Avatar de usuario de Chris
cris

Reaccionar enrutador 4

Puede invocar fácilmente el método push a través del contexto en v4:

this.context.router.push(this.props.exitPath);

donde contexto es:

static contextTypes = {
    router: React.PropTypes.object,
};

  • Usando BrowserRouterel objeto de contexto de mis componentes no contiene un router objeto. ¿Estoy haciendo algo mal?

    – pilau

    1 de marzo de 2017 a las 17:54

  • ¿Está configurando el contexto en el componente (el segundo bloque de arriba)?

    – Chris

    02/03/2017 a las 0:00

  • ¡Gracias por participar! finalmente esto funcionó para mí: router: React.PropTypes.object.isRequired. No sé por qué no funcionó sin el isRequired llave. También, <Link> parece ser capaz de conseguir el history contexto, pero no pude replicarlo.

    – pilau

    2 de marzo de 2017 a las 13:36

  • Interesante: si coloca un codepen, podría ayudarlo a depurarlo si todavía está atascado

    – Chris

    2 de marzo de 2017 a las 13:49

  • Parece que puedes usar this.props.history.push() en React Router v4. Sin embargo, solo encontré esto al inspeccionar los accesorios que pasa React Router. Parece que funciona, pero no estoy seguro de si es una buena idea.

    – sean_j_roberts

    16 de marzo de 2017 a las 14:51

¿Ha sido útil esta solución?