Dans la version actuelle de React Router (v3), je peux accepter une réponse du serveur et utiliser browserHistory.Push
pour accéder à la page de réponse appropriée. Cependant, ce n'est pas disponible dans la v4, et je ne suis pas sûr de la manière appropriée de le gérer.
Dans cet exemple, en utilisant Redux, components/app-product-form.js appelle this.props.addProduct(props)
lorsqu'un utilisateur soumet le formulaire. Lorsque le serveur renvoie un succès, l'utilisateur est dirigé vers la page Panier.
// 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
});
}
Comment puis-je effectuer une redirection vers la page Panier depuis la fonction React Router v4?
Vous pouvez utiliser les méthodes history
en dehors de vos composants. Essayez de la manière suivante.
Tout d’abord, créez un objet history
utilisé le package d’historique :
// src/history.js
import { createBrowserHistory } from 'history';
export default createBrowserHistory();
Enveloppez-le ensuite dans <Router>
(veuillez noter, vous devriez utiliser import { Router }
au lieu 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'),
);
Changez votre position actuelle depuis n'importe quel endroit, par exemple:
// src/actions/userActionCreators.js
// ...
import history from '../history';
export function login(credentials) {
return function (dispatch) {
return loginRemotely(credentials)
.then((response) => {
// ...
history.Push('/');
});
};
}
UPD: Vous pouvez également voir un exemple légèrement différent dans React Router FAQ .
React Router v4 est fondamentalement différent de v3 (et des versions antérieures) et vous ne pouvez plus utiliser browserHistory.Push()
comme auparavant.
_ { Cette discussion } _ semble lié si vous voulez plus d'informations:
- La création d'une nouvelle
browserHistory
ne fonctionnera pas car<BrowserRouter>
crée sa propre instance d'historique et écoute les modifications qui y sont apportées. Donc, une instance différente changera l’URL mais ne mettra pas à jour le<BrowserRouter>
.browserHistory
n'est pas exposé par react-router dans la v4, uniquement dans la v2.
Au lieu de cela, vous avez quelques options pour faire ceci:
withRouter
Au lieu de cela, vous devez utiliser le composant d'ordre supérieur withRouter
et le renvoyer au composant qui transmettra à l'historique. Par exemple:
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);
Consultez le documentation officielle pour plus d'informations:
Vous pouvez accéder aux propriétés de l’objet
history
et au plus proche<Route>
match
via le composant d’ordre supérieur withRouter. withRouter rendra à nouveau son composant à chaque changement d'itinéraire avec les mêmes propriétés que<Route>
render props:{ match, location, history }
.
context
Utiliser le contexte peut être l’une des solutions les plus simples, mais étant une API expérimentale, elle est instable et non prise en charge. Utilisez-le uniquement lorsque tout le reste échoue. Voici un exemple:
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");
}
...
}
Regardez le documentation officielle sur le contexte:
Si vous voulez que votre application soit stable, n'utilisez pas le contexte. Il s’agit d’une API expérimentale qui risque de ne plus fonctionner dans les futures versions de React.
Si vous insistez pour utiliser le contexte malgré ces avertissements, essayez d'isoler votre utilisation du contexte dans une petite zone et évitez d'utiliser l'API de contexte directement lorsque cela est possible, afin de faciliter la mise à niveau lorsque l'API change.
Voici comment je l'ai fait:
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>
);
}
}
Utilisez this.props.history.Push('/cart');
pour rediriger vers la page du panier. Il sera enregistré dans un objet historique.
Profitez-en, Michael.
Mauvaise question, ça m'a pris pas mal de temps, mais finalement, je l'ai résolu de cette façon:
Enveloppez votre conteneur avec withRouter
et transmettez l'historique à votre action dans la fonction mapDispatchToProps
. En action, utilisez history.Push ('/ url') pour naviguer.
Action:
export function saveData(history, data) {
fetch.post('/save', data)
.then((response) => {
...
history.Push('/url');
})
};
Récipient:
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));
Ceci est valable pour React Router v4.x .
this.context.history.Push
ne fonctionnera pas.
J'ai réussi à faire fonctionner Push de la manière suivante:
static contextTypes = {
router: PropTypes.object
}
handleSubmit(e) {
e.preventDefault();
if (this.props.auth.success) {
this.context.router.history.Push("/some/Path")
}
}
Dans ce cas, vous passez des accessoires à votre thunk. Donc, vous pouvez simplement appeler
props.history.Push('/cart')
Si ce n'est pas le cas, vous pouvez toujours transmettre l'historique de votre composant.
export function addProduct(data, history) {
return dispatch => {
axios.post('/url', data).then((response) => {
dispatch({ type: types.AUTH_USER })
history.Push('/cart')
})
}
}
J'offre une solution supplémentaire au cas où cela serait utile pour quelqu'un d'autre.
J'ai un fichier history.js
où j'ai les éléments suivants:
import createHistory from 'history/createBrowserHistory'
const history = createHistory()
history.pushLater = (...args) => setImmediate(() => history.Push(...args))
export default history
Ensuite, sur ma racine où je définis mon routeur, j'utilise les éléments suivants:
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>
)
}
}
Enfin, sur mon actions.js
, j'importe l'historique et utilise pushLater
import history from './history'
export const login = createAction(
...
history.pushLater({ pathname: PATH_REDIRECT_LOGIN })
...)
De cette façon, je peux pousser vers de nouvelles actions après des appels d'API.
J'espère que ça aide!
Utiliser le rappel. Cela a fonctionné pour moi!
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 composant, il vous suffit d'ajouter le rappel
this.props.addProduct(props, () => this.props.history.Push('/cart'))
Si vous utilisez Redux, je vous recommande d'utiliser le paquet npm react-router-redux Il vous permet de répartir les actions de navigation du magasin Redux.
Vous devez créer le magasin comme décrit dans le fichier Readme .
Le cas d'utilisation le plus simple:
import { Push } from 'react-router-redux'
this.props.dispatch(Push('/second page'));
Deuxième cas d'utilisation avec conteneur/composant:
Récipient:
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);
Composant:
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
);
}
}
Le routeur React V4 permet désormais d’utiliser l’historique comme suit:
this.props.history.Push("/dummy",value)
La valeur peut alors être consultée partout où l'emplacement prop est disponible sous la forme state:{value}
pas de composant.
Voici mon hack (c'est mon fichier de niveau racine, avec un peu de redux mélangé dedans - bien que je n'utilise pas 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')
)
Je peux ensuite utiliser window.appHistory.Push()
n'importe où (par exemple, dans les fonctions de mon magasin redux/thunks/sagas, etc.) J'espérais pouvoir utiliser window.customHistory.Push()
mais, pour une raison quelconque, react-router
n'a jamais semblé être mis à jour, même si l'URL a changé. Mais de cette façon, j'ai l'instance EXACT react-router
utilise. Je n'aime pas mettre des choses dans le champ global, et c'est l'une des rares choses avec lesquelles je ferais ça. Mais c'est mieux que toute autre alternative que j'ai vu à l'OMI.
/*Step 1*/
myFunction(){ this.props.history.Push("/home"); }
/**/
<button onClick={()=>this.myFunction()} className={'btn btn-primary'}>Go
Home</button>
vous pouvez l'utiliser comme cela que je le fais pour la connexion et manny différentes choses
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>
)
ainsi, je le fais comme suit: - au lieu de rediriger avec history.Push
, je n'utilise que le composant Redirect
à partir de react-router-dom
Lorsque vous utilisez ce composant, vous pouvez simplement passer Push=true
et il se chargera du reste
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}/>
}
}
première étape pour envelopper votre application dans le routeur
import { BrowserRouter as Router } from "react-router-dom";
ReactDOM.render(<Router><App /></Router>, document.getElementById('root'));
Maintenant, toute mon application aura accès à BrowserRouter. Deuxième étape, j'importe Route, puis je transmets ces accessoires. Probablement dans l'un de vos fichiers principaux.
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>
)}
/>
Maintenant, si je lance console.log (this.props) dans mon fichier composant js, je devrais obtenir quelque chose qui ressemble à ceci
{match: {…}, location: {…}, history: {…}, //other stuff }
Étape 2 Je peux accéder à l'objet d'historique pour changer d'emplacement.
//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
Aussi, je suis juste un étudiant en bootcamp de codage, donc je ne suis pas un expert, mais je sais que vous pouvez aussi vous en servir
window.location = "/" //wherever you want to go
Corrigez-moi si je me trompe, mais lorsque j'ai testé cela, la page entière a été rechargée, ce qui, à mon avis, a complètement annulé l'utilisation de React.
Créez une Router
personnalisée avec sa propre 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
Ensuite, sur votre racine où vous définissez votre Router
, utilisez ce qui suit:
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>
)
}
}
Enfin, importez history
où vous en avez besoin et utilisez-le:
import { history } from '../routers/ExtBrowserRouter';
...
export function logout(){
clearTokens();
history.Push('/login'); //WORKS AS EXPECTED!
return Promise.reject('Refresh token has expired');
}