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

131

Soy nuevo en ReactJS y en React-Router. Tengo un componente que recibe a través de accesorios un <Link/>objeto de react-router . Cada vez que el usuario hace clic en el botón 'siguiente' dentro de este componente, quiero invocar el <Link/>objeto manualmente.

En este momento, estoy usando referencias para acceder a la instancia de respaldo y haciendo clic manualmente en la etiqueta 'a' que <Link/>genera.

Pregunta: ¿Hay alguna forma 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>
         ...
      );
   }
});
...
Alan Souza
fuente

Respuestas:

199

React Router v4 - Redirect Component (actualizado 15/04/2017)

La forma recomendada de v4 es permitir que su método de renderizado atrape una redirección. Use el estado o los accesorios para determinar si el componente de redireccionamiento debe mostrarse (que luego desencadena 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 de referencia del enrutador

También puede aprovechar Routerel contexto que está expuesto al componente Reaccionar.

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 debajo del 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 y BrowserRouterluego exponerla historycomo 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 notables con los ciclos de vida, pero siempre debe volver a generar el enrutador, al igual que en v2. Esto puede ser útil para 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');

Lo último BrowserRouteren extenderse: https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/modules/BrowserRouter.js

React Router v2

Empuje un nuevo estado a la browserHistoryinstancia:

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

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

Matt Lo
fuente
77
hashHistory.push ('/ muestra'); si está utilizando hashHistory en lugar de browserHistory
sanath_p
1
Esto es especialmente útil en la biblioteca material-ui ya que el uso de containerElement = {<Link to = "/" />} no siempre invoca el enlace
Vishal Disawar
2
Tenga en cuenta que con la opción de redirección debe especificar push (es decir, <Redirect push />) Por defecto, hará un reemplazo que no es lo mismo que invocar manualmente un Enlace
aw04
1
@jokab puede usar <NavLink /> en lugar de <Link /> github.com/ReactTraining/react-router/blob/master/packages/…
Matt Lo
1
Redirect no funciona para mí, pero la solución aw04 con withRouter es más simple y funciona
stackdave
90

React Router 4 incluye un withRouter HOC que le da acceso al historyobjeto 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('/')
  }

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

export default withRouter(Foo)
aw04
fuente
9
Esto funcionó para mí y parece la solución más simple.
Rubycut
55
Esta es la mejor solución. No entiendo por qué tiene tan pocos votos.
Benoit
1
Sí, puede hacer clic en el enlace varias veces y el navegador no funcionará. necesitará hacer clic en el navegador varias veces para realmente volver atrás
Vladyslav Tereshyn
@VladyslavTereshyn puede agregar alguna lógica condicional: if ((this.props.location.pathname + this.props.location.search)! == navigationToPath) {...}
MattWeiler
15

En la versión 5.x , puede usar el useHistorygancho 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>
  );
}
Paulo Mateus
fuente
Esta es la mejor solución. Si agrega alguna 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
Necesitaba declarar historyvariable, la invocación de directorios useHistory().pushno está permitida por las reglas de ganchos
onmyway133
Esta parece ser la solución de reacción más moderna.
Youngjae
8

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

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

window.location.assign("/sample");
grechut
fuente
A medida que cambien las líneas de código, su respuesta será mejor si copia los detalles y explica su respuesta aquí. Además, assignno es una propiedad, es una función.
WiredPrairie
(Pero todavía tiene un enlace a una línea específica de un archivo). Incluya la sugerencia específica en su respuesta, y no solo un enlace.
WiredPrairie
Gracias por tu respuesta @grechut. Sin embargo, quiero asegurarme de que Document no sepa nada sobre el enrutador. El comportamiento que espero es: 'Si el usuario hace clic en la flecha derecha, invoque la siguiente función'. La siguiente función puede ser un enlace o no.
Alan Souza
Tengo un par de páginas manejadas fuera de React (pantallas de inicio de sesión con FB y redirecciones de Google), así que necesitaba esto en el navegador para esas páginas desde "browserHistory.push ('/ home');" solo cambió la URL, no pudo enrutar las páginas. Gracias.
Deborah el
77
Esto volvería a cargar la página @grechut, no un comportamiento deseado para una aplicación con enrutador de reacción.
Abhas
2

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

Ahora, en lugar de enviar <Link/>como accesorio al documento, envío <NextLink/>un envoltorio personalizado para el enlace del enrutador de reacción. Al hacer eso, puedo tener la flecha hacia la derecha como parte de la estructura de Enlace sin dejar de tener el 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 utilizarla en this.context.router.transitionTolugar de this.context.transitionTo. Este código funcionará bien para react-router versión 0.12.X.

Alan Souza
fuente
2

React Router 4

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

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

donde el contexto es:

static contextTypes = {
    router: React.PropTypes.object,
};
Chris
fuente
Utilizando BrowserRouter, el objeto de contexto de mis componentes no contiene un routerobjeto. ¿Estoy haciendo algo mal?
pilau
¿Está configurando el contexto en el componente (el segundo bloque anterior)?
Chris
Gracias por sonar! con el tiempo esto funcionó para mí: router: React.PropTypes.object.isRequired. No sé por qué no funcionó sin la isRequiredllave. Además, <Link>parece ser capaz de obtener el historycontexto, pero no pude replicarlo.
pilau
Interesante: si colocas un codepen, podría ayudarte a depurarlo si todavía estás atascado
Chris
Parece que puedes usar this.props.history.push()en React Router v4. Sin embargo, solo encontré esto inspeccionando los accesorios por los que pasa React Router. Parece funcionar, pero no estoy seguro de si es una buena idea.
sean_j_roberts
-1

de nuevo, esto es JS :) esto todavía funciona ...

var linkToClick = document.getElementById('something');
linkToClick.click();

<Link id="something" to={/somewhaere}> the link </Link>
taggartJ
fuente