react-router volver una página ¿cómo se configura el historial?


121

¿Alguien puede decirme cómo puedo volver a la página anterior en lugar de una ruta específica?

Al usar este código:

var BackButton = React.createClass({

 mixins: [Router.Navigation],
  render: function() {
    return (
        <button
            className="button icon-left"
            onClick={this.navigateBack}>
            Back
        </button>
    );
  },

  navigateBack: function(){
    this.goBack();
  }
});

Obtiene este error, se ignoró goBack () porque no hay historial del enrutador

Aquí están mis rutas:

// Routing Components
Route = Router.Route;
RouteHandler = Router.RouteHandler;
DefaultRoute = Router.DefaultRoute;

var routes = (
 <Route name="app" path="/" handler={OurSchoolsApp}>
     <DefaultRoute name="home" handler={HomePage} />
     <Route name="add-school" handler={AddSchoolPage}  />
     <Route name="calendar" handler={CalendarPage}  />
     <Route name="calendar-detail" path="calendar-detail/:id" handler={CalendarDetailPage} />
     <Route name="info-detail" path="info-detail/:id" handler={InfoDetailPage} />
     <Route name="info" handler={InfoPage} />
     <Route name="news" handler={NewsListPage} />
     <Route name="news-detail" path="news-detail/:id" handler={NewsDetailPage} />
     <Route name="contacts" handler={ContactPage} />
     <Route name="contact-detail" handler={ContactDetailPage} />
     <Route name="settings" handler={SettingsPage} />
 </Route>
 );

 Router.run(routes, function(Handler){
   var mountNode = document.getElementById('app');
   React.render(<Handler /> , mountNode);
 });

Si ha encontrado una solución, por favor, compártala aquí. Gracias.
user261002

Respuestas:


43

Creo que sólo necesita habilitar BrowserHistory en el enrutador mediante intializing de esa manera: <Router history={new BrowserHistory}>.

Antes de eso, debe solicitar BrowserHistoryde'react-router/lib/BrowserHistory'

Espero que eso ayude !

ACTUALIZACIÓN: ejemplo en ES6

const BrowserHistory = require('react-router/lib/BrowserHistory').default;

const App = React.createClass({
    render: () => {
        return (
            <div><button onClick={BrowserHistory.goBack}>Go Back</button></div>
        );
    }
});

React.render((
    <Router history={BrowserHistory}>
        <Route path="/" component={App} />
    </Router>
), document.body);

hola, nos enfrentamos al mismo problema. ¿Podría explicarnos más en detalle? GRACIAS
user261002

23
¿Cómo es esta la respuesta correcta? Esto ni siquiera responde a la pregunta. @bomber
GN.

14
Para cualquiera que lea esto. Si desea hacer esto desde un componente secundario. Puede encontrar el objeto histórico en this.props.history. Entonces, el código se convierte enthis.props.history.goBack
Sisir

3
¿Qué hay de react-router 4? No creo que ya sea compatible con BrowserHistory.
Akshay Lokur

1
A partir de la versión 5 de react-router-dom, el historial es creado por BrowserRouter implícitamente y está disponible a través de los accesorios, puede acceder a él a través de props.history.
Srikanth Kyatham

97

Actualización con React v16 y ReactRouter v4.2.0 (octubre de 2017):

class BackButton extends Component {
  static contextTypes = {
    router: () => true, // replace with PropTypes.object if you use them
  }

  render() {
    return (
      <button
        className="button icon-left"
        onClick={this.context.router.history.goBack}>
          Back
      </button>
    )
  }
}

Actualización con React v15 y ReactRouter v3.0.0 (agosto de 2016):

var browserHistory = ReactRouter.browserHistory;

var BackButton = React.createClass({
  render: function() {
    return (
      <button
        className="button icon-left"
        onClick={browserHistory.goBack}>
        Back
      </button>
    );
  }
});

Creé un violín con un ejemplo un poco más complejo con un iframe incrustado: https://jsfiddle.net/kwg1da3a/

React v14 y ReacRouter v1.0.0 (10 de septiembre de 2015)

Puedes hacerlo:

var React = require("react");
var Router = require("react-router");

var SomePage = React.createClass({
  ...

  contextTypes: {
    router: React.PropTypes.func
  },
  ...

  handleClose: function () {
    if (Router.History.length > 1) {
      // this will take you back if there is history
      Router.History.back();
    } else {
      // this will take you to the parent route if there is no history,
      // but unfortunately also add it as a new route
      var currentRoutes = this.context.router.getCurrentRoutes();
      var routeName = currentRoutes[currentRoutes.length - 2].name;
      this.context.router.transitionTo(routeName);
    }
  },
  ...

Debe tener cuidado de tener el historial necesario para volver. Si presiona la página directamente y luego regresa, lo llevará de regreso al historial del navegador antes de su aplicación.

Esta solución se encargará de ambos escenarios. Sin embargo, no manejará un iframe que pueda navegar dentro de la página (y agregar al historial del navegador), con el botón Atrás. Francamente, creo que es un error en el enrutador de reacción. Problema creado aquí: https://github.com/rackt/react-router/issues/1874


¿Sería bueno si hubiera una manera de hacer esto sin usar el contexto?
Adam D

1
Además, dependiendo de la configuración, podría estar usando this.props.history.goBack en lugar de this.context.router.history.goBack
PhoenixB

54
  1. importar withRouter

    import { withRouter } from 'react-router-dom';
  2. Exporta tu componente como:

    export withRouter(nameofcomponent) 
  3. Ejemplo, al hacer clic en el botón, llame a goBack:

    <button onClick={this.props.history.goBack}>Back</button>

Probado en react-router-domv4.3


2
Por cierto, me funciona incluso sin importar o usar withRouter. Quizás solo estamos usando la API nativa del historial del navegador. ¿Pero está bien?
Gianfranco P.

1
Esa es una buena solución. El único problema cuando el usuario llega a la página usando una URL directa -> romperá la aplicación ya que no tiene historial.
skryvets

2
La mejor explicación aquí
Z_z_Z

@MichaelFreidgeim, ¿puede cargar su fragmento de código para que lo revise y verifique?
gaurav makwana

43
this.context.router.goBack()

¡No se requiere combinación de navegación!


1
Esto todavía funciona en React Router v4, pero es this.context.router.history.goBack. (+ historia)
Ben Gotow

13
Con react-router-dom: "v4.2.2" y import { withRouter } from 'react-router-dom';funciona conthis.props.history.goBack();
felansu

1
además del comentario de @felansu, también debe exportar su componente con el enrutador como se explica en la respuesta stackoverflow.com/a/49130220/3123338
Mister Q

32

Método ES6 sin mixins usando react-router, función sin estado.

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

export const Test = () => (
  <div className="">
    <button onClick={browserHistory.goBack}>Back</button>
  </div>
)

3
Recibo una advertencia en el navegador al probar su solución:export 'browserHistory' was not found in 'react-router'
Ralph David Abernathy

2
browserHistory solo existe en v2 y v3. Si usa v4, debe leer la guía de migración: github.com/ReactTraining/react-router/blob/…
ptorsson

@Ralp, si recibe este mensaje de error, consulte esto: stackoverflow.com/questions/49787659/…
Miles M.

13

Usando React Hooks

Importar:

import { useHistory } from "react-router-dom";

En componente apátrida:

  let history = useHistory();

En evento

history.goBack()

<button onClick = {() => history.goBack ()}> Atrás </button>
Hunter

Gracias por mencionar esto porque me gustan los ganchos. He estado usando withRouter hasta ahora.
Newman

10

Vea mi ejemplo de trabajo usando React 16.0 con React-router v4 Live Example . mira el código Github

Utilice withRouteryhistory.goBack()

Esta es la idea que estoy implementando ...

History.js

import React, { Component } from 'react';
import { withRouter } from 'react-router-dom'
import './App.css'


class History extends Component {

  handleBack = () => {
    this.props.history.goBack()
  }

  handleForward = () => {
    console.log(this.props.history)
    this.props.history.go(+1)
  }

  render() {
    return <div className="container">
      <div className="row d-flex justify-content-between">
        <span onClick={this.handleBack} className="d-flex justify-content-start button">
          <i className="fas fa-arrow-alt-circle-left fa-5x"></i>
        </span>
        <span onClick={this.handleForward} className="d-flex justify-content-end button">
          <i className="fas fa-arrow-alt-circle-right fa-5x"></i>
        </span>
      </div>
    </div>
  }
}

export default withRouter(History)

PageOne.js

import React, { Fragment, Component } from 'react'

class PageOne extends Component {

   componentDidMount(){
      if(this.props.location.state && this.props.location.state.from != '/pageone')
      this.props.history.push({
         pathname: '/pageone',
         state: { 
             from: this.props.location.pathname
         }
       });
   }

   render() {
      return (
         <Fragment>
            <div className="container-fluid">
               <div className="row d-flex justify-content-center">
                  <h2>Page One</h2>
               </div>
            </div>
         </Fragment>
      )
   }
}

export default PageOne

ps lo siento, el código es demasiado grande para publicarlo todo aquí


1
Esta debería ser la respuesta aceptada a partir de '19. React Router proporciona un componente de orden superior (HOC) llamado "withRouter" para que pueda envolver su componente para tener acceso a accesorios como el historial y la ubicación.

1
sí, como en withRouter en HOC en su envoltura de su componente, proporcionará el historial y los datos de ubicación. Nada desafiante sigue leyendo y nunca se conforma.
Anupam Maurya

¿Puedo preguntar por qué pasa el signo más a la gofunción history.go(+1):? history.go(1)debería ser suficiente.
gion_13

10

Esto funciona con el historial del navegador y del hash.

this.props.history.goBack();

8

Este es un componente BackButton funcional (React 0.14):

var React = require('react');
var Router = require('react-router');

var History = Router.History;

var BackButton = React.createClass({
  mixins: [ History ],
  render: function() {
    return (
      <button className="back" onClick={this.history.goBack}>{this.props.children}</button>
    );
  }
});

module.exports = BackButton;

Por supuesto, puede hacer algo como esto si no hay historial:

<button className="back" onClick={goBack}>{this.props.children}</button>

function goBack(e) {
  if (/* no history */) {
    e.preventDefault();
  } else {
    this.history.goBack();
  }
}

7

Para react-router v2.x esto ha cambiado. Esto es lo que estoy haciendo para ES6:

import React from 'react';
import FontAwesome from 'react-fontawesome';
import { Router, RouterContext, Link, browserHistory } from 'react-router';

export default class Header extends React.Component {

  render() {
    return (
      <div id="header">
        <div className="header-left">
          {
            this.props.hasBackButton &&
            <FontAwesome name="angle-left" className="back-button" onClick={this.context.router.goBack} />
          }
        </div>
        <div>{this.props.title}</div>
      </div>
    )
  }
}

Header.contextTypes = {
  router: React.PropTypes.object
};

Header.defaultProps = {
  hasBackButton: true
};

Header.propTypes = {
  title: React.PropTypes.string
};

5

En react-router v4.x puede usar lo history.goBackque equivale a history.go(-1).

App.js

import React from "react";
import { render } from "react-dom";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import Home from "./Home";
import About from "./About";
import Contact from "./Contact";
import Back from "./Back";

const styles = {
  fontFamily: "sans-serif",
  textAlign: "left"
};

const App = () => (
  <div style={styles}>
    <Router>
      <div>
        <ul>
          <li><Link to="/">Home</Link></li>
          <li><Link to="/about">About</Link></li>
          <li><Link to="/contact">Contact</Link></li>
        </ul>

        <hr />

        <Route exact path="/" component={Home} />
        <Route path="/about" component={About} />
        <Route path="/contact" component={Contact} />

        <Back />{/* <----- This is component that will render Back button */}
      </div>
    </Router>
  </div>
);

render(<App />, document.getElementById("root"));

Back.js

import React from "react";
import { withRouter } from "react-router-dom";

const Back = ({ history }) => (
  <button onClick={history.goBack}>Back to previous page</button>
);

export default withRouter(Back);

Demostración: https://codesandbox.io/s/ywmvp95wpj

Recuerde que al utilizar historysus usuarios pueden salir porque history.goBack()pueden cargar una página que el visitante ha visitado antes de abrir su aplicación.


Para evitar una situación como la descrita anteriormente, he creado una biblioteca simple react-router-last-location que observa la última ubicación de sus usuarios.

El uso es muy sencillo. Primero necesitas instalar react-router-domy react-router-last-locationdesde npm.

npm install react-router-dom react-router-last-location --save

Luego use de la LastLocationProvidersiguiente manera:

App.js

import React from "react";
import { render } from "react-dom";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import { LastLocationProvider } from "react-router-last-location";
//              ↑
//              |
//              |
//
//       Import provider
//
import Home from "./Home";
import About from "./About";
import Contact from "./Contact";
import Back from "./Back";

const styles = {
  fontFamily: "sans-serif",
  textAlign: "left"
};

const App = () => (
  <div style={styles}>
    <h5>Click on About to see your last location</h5>
    <Router>
      <LastLocationProvider>{/* <---- Put provider inside <Router> */}
        <div>
          <ul>
            <li><Link to="/">Home</Link></li>
            <li><Link to="/about">About</Link></li>
            <li><Link to="/contact">Contact</Link></li>
          </ul>

          <hr />

          <Route exact path="/" component={Home} />
          <Route path="/about" component={About} />
          <Route path="/contact" component={Contact} />

          <Back />
        </div>
      </LastLocationProvider>
    </Router>
  </div>
);

render(<App />, document.getElementById("root"));

Back.js

import React from "react";
import { Link } from "react-router-dom";
import { withLastLocation } from "react-router-last-location";
//              ↑
//              |
//              |
//
//    `withLastLocation` higher order component
//    will pass `lastLocation` to your component               
//
//                   |
//                   |
//                   ↓
const Back = ({ lastLocation }) => (
  lastLocation && <Link to={lastLocation || '/'}>Back to previous page</Link>
);


//          Remember to wrap
//   your component before exporting
//
//                   |
//                   |
//                   ↓
export default withLastLocation(Back);

Demostración: https://codesandbox.io/s/727nqm99jj


¿Cómo introduciría el objeto LastLocation en el componente que lo necesita? Es decir, me gustaría usarlo programáticamente, en lugar de en algún resultado mostrado como sus ejemplos: if (lastLocation == 'about')
dmayo

No estoy seguro de haberlo entendido correctamente, así que corríjame si me equivoco. ¿Le gustaría usarlo lastLocationfuera de React o tal vez diciendo I'd like to use it programmatically, simplemente desea tener la capacidad de importarlo como import { lastLocation } from '...'?
hinok

5

Lo que funcionó para mí fue importar withRouter en la parte superior de mi archivo;

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

Luego utilícelo para envolver la función exportada en la parte inferior de mi archivo;

export default withRouter(WebSitePageTitleComponent)

Lo que luego me permitió acceder al accesorio de historial del enrutador. ¡Código de muestra completo a continuación!

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

import PropTypes from 'prop-types'

class TestComponent extends Component {
  constructor(props) {
    super(props)
    this.handleClick = this.handleClick.bind(this)
  }

  handleClick() {
    event.preventDefault()
    this.props.history.goBack()
  }

  render() {
    return (
      <div className="page-title">
        <a className="container" href="/location" onClick={this.handleClick}>
          <h1 className="page-header">
            { this.props.title }
          </h1>
        </a>
      </div>
    )
  }
}

const { string, object } = PropTypes

TestComponent.propTypes = {
  title: string.isRequired,
  history: object
}

export default withRouter(TestComponent)

5

Regrese a la página específica :

  import { useHistory } from "react-router-dom";

  const history = useHistory();
  
  const routeChange = () => {
    let path = '/login';
    history.push(path);
  };

Vuelve a la página anterior :

  import { useHistory } from "react-router-dom";

  const history = useHistory();
  
  const routeChange = () => {
    history.goBack()
  };

2
gracias, esta es una gran respuesta 2020! La respuesta aceptada fue tan 2015.
beeftosino

3
import { withRouter } from 'react-router-dom'

this.props.history.goBack();

Estoy usando estas versiones

"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",

2

La única solución que funcionó para mí fue la más simple. No se necesitan importaciones adicionales.

<a href="#" onClick={() => this.props.history.goBack()}>Back</a>

Tks, IamMHussain


Muy agradable. Simple y humilde
Dinesh Kanivu

1

Llame al siguiente componente así:

<BackButton history={this.props.history} />

Y aquí está el componente:

import React, { Component } from 'react'
import PropTypes from 'prop-types'
class BackButton extends Component {
  constructor() {
    super(...arguments)

    this.goBack = this.goBack.bind(this)
  }

  render() {
    return (
      <button
        onClick={this.goBack}>
          Back
      </button>
    )
  }

  goBack() {
    this.props.history.goBack()
  }
}

BackButton.propTypes = {
  history: PropTypes.object,
}

export default BackButton

Estoy usando:

"react": "15.6.1"
"react-router": "4.2.0"

1

REDUX

También puede usar react-router-reduxwhich has goBack()y push().

Aquí hay un paquete de muestra para eso:

En el punto de entrada de su aplicación, lo necesita ConnectedRouter, y el historyobjeto es una conexión a veces difícil de conectar . El middleware de Redux escucha los cambios del historial:

import React from 'react'
import { render } from 'react-dom'
import { ApolloProvider } from 'react-apollo'
import { Provider } from 'react-redux'
import { ConnectedRouter } from 'react-router-redux'
import client from './components/apolloClient'
import store, { history } from './store'
import Routes from './Routes'
import './index.css'

render(
  <ApolloProvider client={client}>
    <Provider store={store}>
      <ConnectedRouter history={history}>
        <Routes />
      </ConnectedRouter>
    </Provider>
  </ApolloProvider>,
  document.getElementById('root'),
)

Te mostraré una forma de conectar el history. Observe cómo el historial se importa a la tienda y también se exporta como singleton para que pueda usarse en el punto de entrada de la aplicación:

import { createStore, applyMiddleware, compose } from 'redux'
import { routerMiddleware } from 'react-router-redux'
import thunk from 'redux-thunk'
import createHistory from 'history/createBrowserHistory'
import rootReducer from './reducers'

export const history = createHistory()

const initialState = {}
const enhancers = []
const middleware = [thunk, routerMiddleware(history)]

if (process.env.NODE_ENV === 'development') {
  const { devToolsExtension } = window
  if (typeof devToolsExtension === 'function') {
    enhancers.push(devToolsExtension())
  }
}

const composedEnhancers = compose(applyMiddleware(...middleware), ...enhancers)
const store = createStore(rootReducer, initialState, composedEnhancers)

export default store

El bloque de ejemplo anterior muestra cómo cargar los react-router-reduxayudantes de middleware que completan el proceso de configuración.

Creo que la siguiente parte es completamente adicional, pero la incluiré en caso de que alguien en el futuro se beneficie:

import { combineReducers } from 'redux'
import { routerReducer as routing } from 'react-router-redux'

export default combineReducers({
  routing, form,
})

Utilizo routerReducertodo el tiempo porque me permite forzar la recarga de componentes que normalmente no lo hacen debido a shouldComponentUpdate. El ejemplo obvio es cuando tiene una barra de navegación que se supone que se actualiza cuando un usuario presiona un NavLinkbotón. Si sigue ese camino, aprenderá que usa el método de conexión de Redux shouldComponentUpdate. Con routerReducer, puede utilizar mapStateToPropspara mapear cambios de ruta en la barra de navegación, y esto hará que se actualice cuando cambie el objeto del historial.

Me gusta esto:

const mapStateToProps = ({ routing }) => ({ routing })

export default connect(mapStateToProps)(Nav)

Perdóneme mientras agrego algunas palabras clave adicionales para las personas: si su componente no se actualiza correctamente, investigue shouldComponentUpdateeliminando la función de conexión y vea si soluciona el problema. Si es así, ingrese routerReducery el componente se actualizará correctamente cuando cambie la URL.

Para terminar, después de hacer todo eso, ¡puedes llamar goBack()o push()cuando quieras!

Pruébelo ahora en algún componente aleatorio:

  1. Importar en connect()
  2. Ni siquiera necesitas mapStateToPropsomapDispatchToProps
  3. Importar en goBack y empujar desde react-router-redux
  4. Llamada this.props.dispatch(goBack())
  5. Llamada this.props.dispatch(push('/sandwich'))
  6. Experimente la emoción positiva

Si necesita más muestras, consulte: https://www.npmjs.com/package/react-router-redux


1

Simplemente usa así

<span onClick={() => this.props.history.goBack()}>Back</span>

0

Esta pieza de código hará el truco por ti.

this.context.router.history.goBack()

0

Reaccionar Router v6

useNavigate Hook es la forma recomendada de volver ahora:

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

function App() {
  const navigate = useNavigate();

  return (
    <>
      <button onClick={() => navigate(-1)}>go back</button>
      <button onClick={() => navigate(1)}>go forward</button>
    </>
  );
}

Ejemplo de Codesandbox

Retroceder / avanzar múltiples entradas de la pila del historial:
<button onClick={() => navigate(-2)}>go two back</button>
<button onClick={() => navigate(2)}>go two forward</button>
Ir a ruta específica:
navigate("users") // go to users route, like history.push
navigate("users", { replace: true }) // go to users route, like history.replace
navigate("users", { state }) // go to users route, pass some state in

useNavigate reemplaza useHistory para admitir mejor el próximo modo React Suspense / Concurrent.


Traté de intentar esto pero recibí el error:Attempted import error: 'useNavigate' is not exported from 'react-router-dom'.
Geek

1
@Geek buena captura. Con v6.0.0-beta.0, los desarrolladores han convertido el historypaquete en una dependencia entre pares. Eche un vistazo a la caja de códigos actualizada, que vuelve a funcionar ahora.
Ford04

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.