reactjs - link - ¿Cómo pasar a la historia en React Router v4?
router example react (19)
Aquí está mi truco (este es mi archivo de nivel raíz, con un poco de redux mezclado allí, aunque no estoy usando
react-router-redux
):
const store = configureStore()
const customHistory = createBrowserHistory({
basename: config.urlBasename || ''''
})
ReactDOM.render(
<Provider store={store}>
<Router history={customHistory}>
<Route component={({history}) => {
window.appHistory = history
return (
<App />
)
}}/>
</Router>
</Provider>,
document.getElementById(''root'')
)
Entonces puedo usar
window.appHistory.push()
cualquier lugar que desee (por ejemplo, en mi tienda redux funciones / thunks / sagas, etc.) Esperaba poder usar
window.customHistory.push()
pero por alguna razón
react-router
nunca pareció actualizarse a pesar de que la URL cambió.
Pero de esta manera tengo la instancia EXACT que utiliza el
react-router
.
No me encanta poner cosas en el ámbito global, y esta es una de las pocas cosas con las que haría eso.
Pero es mejor que cualquier otra alternativa que haya visto IMO.
En la versión actual de React Router (v3) puedo aceptar una respuesta del servidor y usar
browserHistory.push
para ir a la página de respuesta adecuada.
Sin embargo, esto no está disponible en v4, y no estoy seguro de cuál es la forma adecuada de manejar esto.
En este ejemplo, usando Redux,
components / app-product-form.js
llama a
this.props.addProduct(props)
cuando un usuario envía el formulario.
Cuando el servidor devuelve un éxito, el usuario es llevado a la página Carrito.
// actions/index.js
export function addProduct(props) {
return dispatch =>
axios.post(`${ROOT_URL}/cart`, props, config)
.then(response => {
dispatch({ type: types.AUTH_USER });
localStorage.setItem(''token'', response.data.token);
browserHistory.push(''/cart''); // no longer in React Router V4
});
}
¿Cómo puedo hacer una redirección a la página del carrito desde la función React Router v4?
Así es como lo hice:
import React, {Component} from ''react'';
export default class Link extends Component {
constructor(props) {
super(props);
this.onLogout = this.onLogout.bind(this);
}
onLogout() {
this.props.history.push(''/'');
}
render() {
return (
<div>
<h1>Your Links</h1>
<button onClick={this.onLogout}>Logout</button>
</div>
);
}
}
Utilice
this.props.history.push(''/cart'');
para redirigir a la página del carrito se guardará en el objeto de historial
Disfruta, Michael.
Cree un
Router
personalizado con su propio
browserHistory
:
import React from ''react'';
import { Router } from ''react-router-dom'';
import { createBrowserHistory } from ''history'';
export const history = createBrowserHistory();
const ExtBrowserRouter = ({children}) => (
<Router history={history} >
{ children }
</Router>
);
export default ExtBrowserRouter
Luego, en su raíz donde define su
Router
, use lo siguiente:
import React from ''react'';
import { /*BrowserRouter,*/ Route, Switch, Redirect } from ''react-router-dom'';
//Use ''ExtBrowserRouter'' instead of ''BrowserRouter''
import ExtBrowserRouter from ''./ExtBrowserRouter'';
...
export default class Root extends React.Component {
render() {
return (
<Provider store={store}>
<ExtBrowserRouter>
<Switch>
...
<Route path="/login" component={Login} />
...
</Switch>
</ExtBrowserRouter>
</Provider>
)
}
}
Finalmente, importe el
history
donde lo necesite y úselo:
import { history } from ''../routers/ExtBrowserRouter'';
...
export function logout(){
clearTokens();
history.push(''/login''); //WORKS AS EXPECTED!
return Promise.reject(''Refresh token has expired'');
}
De acuerdo con la documentación de React Router v4 - sesión de Redux Deep Integration
Se necesita una integración profunda para:
"poder navegar enviando acciones"
Sin embargo, recomiendan este enfoque como una alternativa a la "integración profunda":
"En lugar de enviar acciones para navegar, puede pasar el objeto de historial proporcionado para enrutar componentes a sus acciones y navegar con él allí".
Entonces puede envolver su componente con el componente de orden superior withRouter:
export default withRouter(connect(null, { actionCreatorName })(ReactComponent));
que pasará la API de historial a los accesorios. Entonces puedes llamar al creador de la acción pasando la historia como un parámetro. Por ejemplo, dentro de su ReactComponent:
onClick={() => {
this.props.actionCreatorName(
this.props.history,
otherParams
);
}}
Luego, dentro de tus acciones / index.js:
export function actionCreatorName(history, param) {
return dispatch => {
dispatch({
type: SOME_ACTION,
payload: param.data
});
history.push("/path");
};
}
En este caso, estás pasando accesorios a tu puño. Entonces puedes simplemente llamar
props.history.push(''/cart'')
Si este no es el caso, aún puede pasar el historial de su componente
export function addProduct(data, history) {
return dispatch => {
axios.post(''/url'', data).then((response) => {
dispatch({ type: types.AUTH_USER })
history.push(''/cart'')
})
}
}
La manera más simple en React Router 4 es usar
this.props.history.push(''/new/url'');
Pero para usar este método, su componente
existente
debe tener acceso al objeto de
history
.
Podemos acceder por
-
Si su componente está vinculado a la
Route
directamente, entonces su componente ya tiene acceso al objeto dehistory
.p.ej:
<Route path="/profile" component={ViewProfile}/>
Aquí
ViewProfile
tiene acceso alhistory
. -
Si no está conectado a la
Route
directamente.p.ej:
<Route path="/users" render={() => <ViewUsers/>}
Luego tenemos que usar
withRouter
, una función de orden superior para deformar el componente existente.ViewUsers
InsideViewUsers
-
import { withRouter } from ''react-router-dom'';
-
export default withRouter(ViewUsers);
Eso es todo, su componente
ViewUsers
tiene acceso al objeto dehistory
. -
ACTUALIZAR
2
: en este escenario, pase todos los
props
ruta a su componente, y luego podemos acceder a
this.props.history
desde el componente incluso sin un
HOC
p.ej:
<Route path="/users" render={props => <ViewUsers {...props} />}
Ofrezco una solución más en caso de que valga la pena para otra persona.
Tengo un archivo
history.js
donde tengo lo siguiente:
import createHistory from ''history/createBrowserHistory''
const history = createHistory()
history.pushLater = (...args) => setImmediate(() => history.push(...args))
export default history
Luego, en mi raíz donde defino mi enrutador, uso lo siguiente:
import history from ''../history''
import { Provider } from ''react-redux''
import { Router, Route, Switch } from ''react-router-dom''
export default class Root extends React.Component {
render() {
return (
<Provider store={store}>
<Router history={history}>
<Switch>
...
</Switch>
</Router>
</Provider>
)
}
}
Finalmente, en my
actions.js
importo Historial y uso pushLater
import history from ''./history''
export const login = createAction(
...
history.pushLater({ pathname: PATH_REDIRECT_LOGIN })
...)
De esta manera, puedo impulsar nuevas acciones después de las llamadas a la API.
¡Espero eso ayude!
Pregunta desagradable, me llevó bastante tiempo, pero finalmente, lo resolví de esta manera:
Envuelva su contenedor
withRouter
y pase el historial a su acción en la función
mapDispatchToProps
.
En acción, use history.push (''/ url'') para navegar.
Acción:
export function saveData(history, data) {
fetch.post(''/save'', data)
.then((response) => {
...
history.push(''/url'');
})
};
Envase:
import { withRouter } from ''react-router-dom'';
...
const mapDispatchToProps = (dispatch, ownProps) => {
return {
save: (data) => dispatch(saveData(ownProps.history, data))}
};
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Container));
Esto es válido para React Router v4.x.
Pude lograr esto usando
bind()
.
Quería hacer clic en un botón en
index.jsx
, publicar algunos datos en el servidor, evaluar la respuesta y redirigir a
success.jsx
.
Así es como lo resolví ...
index.jsx
:
import React, { Component } from "react"
import { postData } from "../../scripts/request"
class Main extends Component {
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
this.postData = postData.bind(this)
}
handleClick() {
const data = {
"first_name": "Test",
"last_name": "Guy",
"email": "[email protected]"
}
this.postData("person", data)
}
render() {
return (
<div className="Main">
<button onClick={this.handleClick}>Test Post</button>
</div>
)
}
}
export default Main
request.js
:
import { post } from "./fetch"
export const postData = function(url, data) {
// post is a fetch() in another script...
post(url, data)
.then((result) => {
if (result.status === "ok") {
this.props.history.push("/success")
}
})
}
success.jsx
:
import React from "react"
const Success = () => {
return (
<div className="Success">
Hey cool, got it.
</div>
)
}
export default Success
Entonces, al vincular
this
a
postData
en
index.jsx
, pude acceder a
this.props.history
en
request.js
... luego puedo reutilizar esta función en diferentes componentes, solo tengo que asegurarme de recordar haber incluido
this.postData = postData.bind(this)
en el
constructor()
.
Puede usar los métodos del
history
fuera de sus componentes.
Intenta de la siguiente manera.
Primero, cree un objeto de
history
que use
el paquete de historial
:
// src/history.js
import { createBrowserHistory } from ''history'';
export default createBrowserHistory();
Luego envuélvala en
<Router>
(
tenga en cuenta
que debe usar
import { Router }
lugar de
import { BrowserRouter as Router }
):
// src/index.jsx
// ...
import { Router, Route, Link } from ''react-router-dom'';
import history from ''./history'';
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/login">Login</Link></li>
</ul>
<Route exact path="/" component={HomePage} />
<Route path="/login" component={LoginPage} />
</div>
</Router>
</Provider>,
document.getElementById(''root''),
);
Cambie su ubicación actual desde cualquier lugar, por ejemplo:
// src/actions/userActionCreators.js
// ...
import history from ''../history'';
export function login(credentials) {
return function (dispatch) {
return loginRemotely(credentials)
.then((response) => {
// ...
history.push(''/'');
});
};
}
UPD : También puede ver un ejemplo ligeramente diferente en las Preguntas frecuentes de React Router .
React Router v4 es fundamentalmente diferente de v3 (y anterior) y no puede hacer
browserHistory.push()
como solía hacerlo.
Esta discusión parece relacionada si quieres más información:
- Crear un nuevo
browserHistory
no funcionará porque<BrowserRouter>
crea su propia instancia de historial y escucha los cambios al respecto. Por lo tanto, una instancia diferente cambiará la URL pero no actualizará el<BrowserRouter>
.browserHistory
no está expuesto por react-router en v4, solo en v2.
En cambio, tiene algunas opciones para hacer esto:
-
Utilice el
withRouter
alto ordenwithRouter
En su lugar, debe usar el componente de orden superior
withRouter
al componente que empujará al historial. Por ejemplo:import React from "react"; import { withRouter } from "react-router-dom"; class MyComponent extends React.Component { ... myFunction() { this.props.history.push("/some/Path"); } ... } export default withRouter(MyComponent);
Consulte la documentación oficial para más información:
Puede obtener acceso a las propiedades del objeto de
history
y a lamatch
más cercana de<Route>
través del componente de orden superior withRouter. withRouter volverá a representar su componente cada vez que la ruta cambie con los mismos accesorios que<Route>
render props:{ match, location, history }
.
-
Usa la API de
context
El uso del contexto podría ser una de las soluciones más fáciles, pero al ser una API experimental, es inestable y no es compatible. Úselo solo cuando todo lo demás falla. Aquí hay un ejemplo:
import React from "react"; import PropTypes from "prop-types"; class MyComponent extends React.Component { static contextTypes = { router: PropTypes.object } constructor(props, context) { super(props, context); } ... myFunction() { this.context.router.history.push("/some/Path"); } ... }
Echa un vistazo a la documentación oficial sobre contexto:
Si desea que su aplicación sea estable, no use el contexto. Es una API experimental y es probable que se rompa en futuras versiones de React.
Si insiste en usar el contexto a pesar de estas advertencias, intente aislar su uso del contexto en un área pequeña y evite usar la API de contexto directamente cuando sea posible para que sea más fácil actualizar cuando la API cambie.
React router V4 ahora permite utilizar el accesorio de historial de la siguiente manera:
this.props.history.push("/dummy",value)
Se puede acceder al valor siempre que el accesorio de ubicación esté disponible como
state:{value}
no estado del componente.
Si está utilizando Redux, entonces recomendaría usar el paquete npm react-router-redux . Le permite despachar acciones de navegación de la tienda Redux.
Debe crear la tienda como se describe en su react-router-redux .
El caso de uso más fácil:
import { push } from ''react-router-redux''
this.props.dispatch(push(''/second page''));
Segundo caso de uso con contenedor / componente:
Envase:
import { connect } from ''react-redux'';
import { push } from ''react-router-redux'';
import Form from ''../components/Form'';
const mapDispatchToProps = dispatch => ({
changeUrl: url => dispatch(push(url)),
});
export default connect(null, mapDispatchToProps)(Form);
Componente:
import React, { Component } from ''react'';
import PropTypes from ''prop-types'';
export default class Form extends Component {
handleClick = () => {
this.props.changeUrl(''/secondPage'');
};
render() {
return (
<div>
<button onClick={this.handleClick}/>
</div>Readme file
);
}
}
Usar devolución de llamada. ¡Funcionó para mí!
export function addProduct(props, callback) {
return dispatch =>
axios.post(`${ROOT_URL}/cart`, props, config)
.then(response => {
dispatch({ type: types.AUTH_USER });
localStorage.setItem(''token'', response.data.token);
callback();
});
}
En el componente, solo tiene que agregar la devolución de llamada
this.props.addProduct(props, () => this.props.history.push(''/cart''))
así que la forma en que lo hago es: - en lugar de redirigir usando
history.push
, solo uso el componente
Redirect
de
react-router-dom
Cuando use este componente, puede pasar
push=true
y se encargará del resto
import * as React from ''react'';
import { Redirect } from ''react-router-dom'';
class Example extends React.Component {
componentDidMount() {
this.setState({
redirectTo: ''/test/path''
});
}
render() {
const { redirectTo } = this.state;
return <Redirect to={{pathname: redirectTo}} push={true}/>
}
}
el primer paso envuelve tu aplicación en el enrutador
import { BrowserRouter as Router } from "react-router-dom";
ReactDOM.render(<Router><App /></Router>, document.getElementById(''root''));
Ahora toda mi aplicación tendrá acceso a BrowserRouter. Paso dos importo Ruta y luego paso esos accesorios. Probablemente en uno de tus archivos principales.
import { Route } from "react-router-dom";
//lots of code here
//somewhere in my render function
<Route
exact
path="/" //put what your file path is here
render={props => (
<div>
<NameOfComponent
{...props} //this will pass down your match, history, location objects
/>
</div>
)}
/>
Ahora, si ejecuto console.log (this.props) en mi archivo js de componente, debería obtener algo similar a esto
{match: {…}, location: {…}, history: {…}, //other stuff }
Paso 2 Puedo acceder al objeto de historial para cambiar mi ubicación
//lots of code here relating to my whatever request I just ran delete, put so on
this.props.history.push("/") // then put in whatever url you want to go to
Además, solo soy un estudiante de programación de bootcamp, así que no soy un experto, pero sé que también puedes usar
window.location = "/" //wherever you want to go
Corrígeme si me equivoco, pero cuando lo probé, volvió a cargar toda la página, lo que pensé que derrotó el punto de usar React.
puedes usarlo así como lo hago para iniciar sesión y muchas cosas diferentes
class Login extends Component {
constructor(props){
super(props);
this.login=this.login.bind(this)
}
login(){
this.props.history.push(''/dashboard'');
}
render() {
return (
<div>
<button onClick={this.login}>login</login>
</div>
)
this.context.history.push
no funcionará.
Me las arreglé para empujar trabajando así:
static contextTypes = {
router: PropTypes.object
}
handleSubmit(e) {
e.preventDefault();
if (this.props.auth.success) {
this.context.router.history.push("/some/Path")
}
}
/*Step 1*/
myFunction(){ this.props.history.push("/home"); }
/**/
<button onClick={()=>this.myFunction()} className={''btn btn-primary''}>Go
Home</button>