tutorial react medium exact español reactjs ecmascript-6 react-router-v4

reactjs - react - Navegue mediante programación usando el router de reacción V4



react-router-dom redirect (14)

Como a veces prefiero cambiar las rutas por aplicación y luego por botones, este es un ejemplo de trabajo mínimo que funciona para mí:

import { Component } from ''react'' import { BrowserRouter as Router, Link } from ''react-router-dom'' class App extends Component { constructor(props) { super(props) /** @type BrowserRouter */ this.router = undefined } async handleSignFormSubmit() { await magic() this.router.history.push(''/'') } render() { return ( <Router ref={ el => this.router = el }> <Link to="/signin">Sign in</Link> <Route path="/signin" exact={true} render={() => ( <SignPage onFormSubmit={ this.handleSignFormSubmit } /> )} /> </Router> ) } }

Acabo de reemplazar react-router de v3 a v4.
Pero no estoy seguro de cómo navegar mediante programación en la función miembro de un Component . es decir, en la función handleClick() quiero navegar a /path/some/where después de procesar algunos datos. Solía ​​hacer eso por:

import { browserHistory } from ''react-router'' browserHistory.push(''/path/some/where'')

Pero no puedo encontrar tales interfaces en v4.
¿Cómo puedo navegar usando v4?


Como no hay otra forma de lidiar con este horrible diseño, escribí un componente genérico que utiliza el enfoque withRouter HOC . El siguiente ejemplo está envolviendo un elemento de button , pero puede cambiar a cualquier elemento en el que pueda hacer clic:

import React from ''react''; import PropTypes from ''prop-types''; import { withRouter } from ''react-router-dom''; const NavButton = (props) => ( <Button onClick={() => props.history.push(props.to)}> {props.children} </Button> ); NavButton.propTypes = { history: PropTypes.shape({ push: PropTypes.func.isRequired }), to: PropTypes.string.isRequired }; export default withRouter(NavButton);

Uso:

<NavButton to="/somewhere">Click me</NavButton>


Creo que @rgommezz cubre la mayoría de los casos menos uno que creo que es bastante importante.

// history is already a dependency or React Router, but if don''t have it then try npm install save-dev history import createHistory from "history/createBrowserHistory" // in your function then call add the below const history = createHistory(); // Use push, replace, and go to navigate around. history.push("/home");

Esto me permite escribir un servicio simple con acciones / llamadas a las que puedo llamar para hacer la navegación desde cualquier componente que desee sin hacer mucho HoC en mis componentes ...

No está claro por qué nadie ha proporcionado esta solución antes. Espero que ayude, y si ve algún problema con esto, hágamelo saber.


Esto funciona:

import { withRouter } from ''react-router-dom''; const SomeComponent = withRouter(({ history }) => ( <div onClick={() => history.push(''/path/some/where'')}> some clickable element </div>); ); export default SomeComponent;


He estado probando v4 durante unos días y ... ¡hasta ahora me encanta! Simplemente tiene sentido después de un tiempo.

También tuve la misma pregunta y encontré que manejarlo como el siguiente funcionó mejor (e incluso podría ser cómo se pretende). Utiliza state, un operador ternario y <Redirect> .

En el constructor ()

this.state = { redirectTo: null } this.clickhandler = this.clickhandler.bind(this);

En el render ()

render(){ return ( <div> { this.state.redirectTo ? <Redirect to={{ pathname: this.state.redirectTo }} /> : ( <div> .. <button onClick={ this.clickhandler } /> .. </div> ) }

En el clickhandler ()

this.setState({ redirectTo: ''/path/some/where'' });

Espero eso ayude. Házmelo saber.


La forma más fácil de hacerlo:

this.props.history.push("/new/url")

Nota:

  • Es posible que desee pasar el prop history del componente principal al componente en el que desea invocar la acción si no está disponible.

Luché con esto por un tiempo, algo tan simple, pero tan complicado, porque ReactJS es una forma completamente diferente de escribir aplicaciones web, ¡es muy extraño para nosotros, las personas mayores!

Creé un componente separado para abstraer el desastre:

// LinkButton.js import React from "react"; import PropTypes from "prop-types"; import {Route} from ''react-router-dom''; export default class LinkButton extends React.Component { render() { return ( <Route render={({history}) => ( <button {...this.props} onClick={() => { history.push(this.props.to) }}> {this.props.children} </button> )}/> ); } } LinkButton.propTypes = { to: PropTypes.string.isRequired };

Luego agréguelo a su método render() :

<LinkButton className="btn btn-primary" to="/location"> Button Text </LinkButton>


Mi respuesta es similar a la de Alex''s . No estoy seguro de por qué React-Router hizo esto tan innecesariamente complicado. ¿Por qué debería tener que envolver mi componente con un HoC solo para obtener acceso a lo que es esencialmente global?

De todos modos, si <BrowserRouter> un vistazo a cómo implementaron <BrowserRouter> , es solo un pequeño contenedor alrededor de la history .

Podemos extraer ese historial para poder importarlo desde cualquier lugar. El truco, sin embargo, es que si está haciendo renderizado del lado del servidor e intenta import el módulo de historial, no funcionará porque usa API solo para el navegador. Pero eso está bien porque generalmente solo redirigimos en respuesta a un clic o algún otro evento del lado del cliente. Por lo tanto, probablemente esté bien simularlo:

// history.js if(__SERVER__) { module.exports = {}; } else { module.exports = require(''history'').createBrowserHistory(); }

Con la ayuda de webpack, podemos definir algunos variables para saber en qué entorno estamos:

plugins: [ new DefinePlugin({ ''__SERVER__'': ''false'', ''__BROWSER__'': ''true'', // you really only need one of these, but I like to have both }),

Y ahora puedes

import history from ''./history'';

De donde sea. Simplemente devolverá un módulo vacío en el servidor.

Si no desea usar estos vars mágicos, solo tendrá que require en el objeto global donde sea necesario (dentro de su controlador de eventos). import no funcionará porque solo funciona en el nivel superior.


Para aquellos de ustedes que necesitan redirigir antes de iniciar completamente un enrutador usando React Router o React Router Dom . Puede proporcionar una redirección simplemente accediendo al objeto de historial y presionando un nuevo estado dentro de su constructor de app.js Considera lo siguiente:

function getSubdomain(hostname) { let regexParse = new RegExp(''[a-z/-0-9]{2,63}/.[a-z/.]{2,5}$''); let urlParts = regexParse.exec(hostname); return hostname.replace(urlParts[0], '''').slice(0, -1); } class App extends Component { constructor(props) { super(props); this.state = { hostState: true }; if (getSubdomain(window.location.hostname).length > 0) { this.state.hostState = false; window.history.pushState('''', '''', ''./login''); } else { console.log(getSubdomain(window.location.hostname)); } } render() { return ( <BrowserRouter> {this.state.hostState ? ( <div> <Route path="/login" component={LoginContainer}/> <Route path="/" component={PublicContainer}/> </div> ) : ( <div> <Route path="/login" component={LoginContainer}/> </div> ) } </BrowserRouter>) } }

Aquí queremos cambiar las Rutas de salida que dependen de un subdominio, al interactuar con el objeto de historial antes de que el componente se procese, podemos redirigir efectivamente sin dejar nuestras rutas intactas.

window.history.pushState('''', '''', ''./login'');


Paso 1: solo hay una cosa para importar:

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

Paso 2: en tu ruta, pasa el historial:

<Route exact path=''/posts/add'' render={({history}) => ( <PostAdd history={history} /> .)}/>

Paso 3: el historial se acepta como parte de los accesorios en el siguiente Componente, por lo que puede simplemente:

this.props.history.push(''/'');

Eso fue fácil y realmente poderoso.


Si se dirige a entornos de navegador, debe usar el paquete react-router-dom , en lugar de react-router . Están siguiendo el mismo enfoque que React, para separar el núcleo, ( react ) y el código específico de la plataforma, ( react-dom , react-native ) con la sutil diferencia de que no necesita instalar dos paquetes separados , por lo que los paquetes de entorno contienen todo lo que necesita. Puedes agregarlo a tu proyecto como:

yarn add react-router-dom

o

npm i react-router-dom

Lo primero que debe hacer es proporcionar un <BrowserRouter> como el principal componente principal en su aplicación. <BrowserRouter> usa la API de history HTML5 y la administra por usted, por lo que no tiene que preocuparse de crear una instancia usted mismo y pasarlo al componente <BrowserRouter> como accesorio (como lo necesitaba hacer en versiones anteriores).

En V4, para navegar programáticamente, necesita acceder al objeto de history , que está disponible a través del context Reaccionar, siempre que tenga un componente de proveedor <BrowserRouter> como el principal padre en su aplicación. La biblioteca expone a través del contexto el objeto router , que en sí mismo contiene el history como una propiedad. La interfaz de history ofrece varios métodos de navegación, como push , replace y goBack , entre otros. Puede consultar la lista completa de propiedades y métodos here .

Nota importante para los usuarios de Redux / Mobx

Si está utilizando redux o mobx como su biblioteca de administración de estado en su aplicación, es posible que haya encontrado problemas con los componentes que deberían tener en cuenta la ubicación pero que no se vuelven a reproducir después de activar una actualización de URL

Eso sucede porque react-router pasa la location a los componentes utilizando el modelo de contexto.

Tanto connect como observador crean componentes cuyos métodos shouldComponentUpdate hacen una comparación superficial de sus accesorios actuales y sus próximos accesorios. Esos componentes solo se volverán a renderizar cuando haya cambiado al menos un accesorio. Esto significa que para garantizar que se actualicen cuando cambie la ubicación, deberán recibir un accesorio que cambie cuando cambie la ubicación.

Los 2 enfoques para resolver esto son:

  • Envuelva su componente conectado en una <Route /> sin <Route /> . El objeto de location actual es uno de los accesorios que un <Route> pasa al componente que representa
  • Envuelva su componente conectado con el componente de orden superior withRouter , que de hecho tiene el mismo efecto e inyecta la location que un accesorio

Dejando eso de lado, hay cuatro formas de navegar programáticamente, ordenadas por recomendación:

1.- Usando un componente <Route>

Promueve un estilo declarativo. Antes de v4, los componentes <Route /> se colocaban en la parte superior de la jerarquía de componentes, teniendo que pensar de antemano en la estructura de sus rutas. Sin embargo, ahora puede tener componentes <Route> en cualquier parte de su árbol, lo que le permite tener un control más preciso para la representación condicional dependiendo de la URL. Route inyecta match , location e history como accesorios en su componente. Los métodos de navegación (como push , replace , goBack ...) están disponibles como propiedades del objeto de history .

Hay 3 formas de renderizar algo con una Route , utilizando component , render o accesorios secundarios, pero no use más de uno en la misma Route . La elección depende del caso de uso, pero básicamente las dos primeras opciones solo mostrarán su componente si la path coincide con la ubicación de la URL, mientras que con los children el componente se mostrará si la ruta coincide con la ubicación o no (útil para ajustar la IU basada en coincidencia de URL).

Si desea personalizar la salida de representación de su componente , debe envolver su componente en una función y usar la opción de render , para pasar a su componente cualquier otro accesorio que desee, además de la match , la location y el history . Un ejemplo para ilustrar:

import { BrowserRouter as Router } from ''react-router-dom'' const ButtonToNavigate = ({ title, history }) => ( <button type="button" onClick={() => history.push(''/my-new-location'')} > {title} </button> ); const SomeComponent = () => ( <Route path="/" render={(props) => <ButtonToNavigate {...props} title="Navigate elsewhere" />} /> ) const App = () => ( <Router> <SomeComponent /> // Notice how in v4 we can have any other component interleaved <AnotherComponent /> </Router> );

2.- Uso con withRouter HoC

Este componente de orden superior inyectará los mismos accesorios que Route . Sin embargo, conlleva la limitación de que solo puede tener 1 HoC por archivo.

import { withRouter } from ''react-router-dom'' const ButtonToNavigate = ({ history }) => ( <button type="button" onClick={() => history.push(''/my-new-location'')} > Navigate </button> ); ButtonToNavigate.propTypes = { history: React.PropTypes.shape({ push: React.PropTypes.func.isRequired, }), }; export default withRouter(ButtonToNavigate);

3.- Usando un componente Redirect

Al representar un <Redirect> se navegará a una nueva ubicación. Pero tenga en cuenta que, de forma predeterminada , la ubicación actual se reemplaza por la nueva, como las redirecciones del lado del servidor (HTTP 3xx). La nueva ubicación es proporcionada por prop, que puede ser una cadena (URL para redirigir a) o un objeto de location . Si desea insertar una nueva entrada en el historial , pase también un accesorio de push configúrelo como true

<Redirect to="/your-new-location" push />

4.- Acceso al router manualmente a través del contexto

Un poco desanimado porque el context sigue siendo una API experimental y es probable que se rompa / cambie en futuras versiones de React

const ButtonToNavigate = (props, context) => ( <button type="button" onClick={() => context.router.history.push(''/my-new-location'')} > Navigate to a new location </button> ); ButtonToNavigate.contextTypes = { router: React.PropTypes.shape({ history: React.PropTypes.object.isRequired, }), };

No hace falta decir que también hay otros componentes del enrutador destinados a ecosistemas que no son del navegador, como <NativeRouter> que replica una pila de navegación en la memoria y apunta a la plataforma React Native, disponible a través react-router-native paquete react-router-native .

Para cualquier referencia adicional, no dude en consultar los documentos oficiales . También hay un video hecho por uno de los coautores de la biblioteca que proporciona una introducción bastante interesante al react-router v4, destacando algunos de los principales cambios.


TL; DR:

if (navigate) { return <Redirect to="/" push={true} /> }

La respuesta simple y declarativa es que debe usar <Redirect to={URL} push={boolean} /> en combinación con setState()

push: boolean: cuando es verdadero, la redirección empujará una nueva entrada al historial en lugar de reemplazar la actual.

import { Redirect } from ''react-router'' class FooBar extends React.Component { state = { navigate: false } render() { const { navigate } = this.state // here is the important part if (navigate) { return <Redirect to="/" push={true} /> } // ^^^^^^^^^^^^^^^^^^^^^^^ return ( <div> <button onClick={() => this.setState({ navigate: true })}> Home </button> </div> ) } }

Ejemplo completo here . Lee más here .

PD. El ejemplo utiliza los inicializadores de propiedades ES7 + para inicializar el estado. Mira here también, si estás interesado.


También puede simplemente usar accesorios: this.props.history.push(''new_url'')


Tuve un problema similar al migrar a React-Router v4, así que intentaré explicar mi solución a continuación.

No considere esta respuesta como la forma correcta de resolver el problema, imagino que hay una buena posibilidad de que surja algo mejor a medida que React Router v4 se vuelva más maduro y deje beta (incluso puede existir y simplemente no lo descubrí) .

Por contexto, tuve este problema porque ocasionalmente uso Redux-Saga para cambiar programáticamente el objeto de historial (por ejemplo, cuando un usuario se autentica con éxito).

En los documentos de React Router, eche un vistazo al component <Router> y verá que tiene la capacidad de pasar su propio objeto de historia a través de un accesorio. Esta es la esencia de la solución: suministramos el objeto de historia a React-Router desde un módulo global .

Pasos:

  1. Instale el módulo de historial npm - yarn add history o npm install history --save
  2. crear un archivo llamado history.js en su carpeta de nivel App.js (esta era mi preferencia)

    // src/history.js import createHistory from ''history/createBrowserHistory''; export default createHistory();`

  3. Agregue este objeto de historial a su componente de enrutador de esta manera

    // src/App.js import history from ''../your/path/to/history.js;'' <Router history={history}> // Route tags here </Router>

  4. Ajuste la URL como antes importando su objeto de historial global:

    import history from ''../your/path/to/history.js;'' history.push(''new/path/here/'');

Todo debería estar sincronizado ahora, y también tiene acceso a una forma de configurar el objeto de historial mediante programación y no a través de un componente / contenedor.