Comment pousser à L'histoire dans React Router V4?
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 v4, et je ne suis pas sûr de la façon appropriée de gérer cela.
Dans cet exemple, à l'aide de Redux, composants/app-produit-forme.js appels this.props.addProduct(props)
lorsqu'un utilisateur soumet le formulaire. Lorsque le serveur renvoie un succès, l'utilisateur est redirigé 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 faire une redirection vers la page du Panier à partir de fonction pour réagir Routeur v4?
15 réponses
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 paquet historique :
// src/history.js
import { createBrowserHistory } from 'history';
export default createBrowserHistory();
, Puis l'envelopper dans <Router>
(veuillez noter, vous devez 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 emplacement actuel de 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 de Réagir Routeur FAQ.
React Router V4 est fondamentalement différent de v3 (et plus tôt) et vous ne pouvez pas faire browserHistory.push()
comme vous le faisiez auparavant.
Cette discussion semble liée, si vous voulez plus d'info:
- la création d'un nouveau
browserHistory
ne fonctionnera pas car<BrowserRouter>
crée sa propre instance d'historique et écoute les modifications à ce sujet. Ainsi, une instance différente changera l'url mais ne mettra pas à jour le<BrowserRouter>
.browserHistory
n'est pas exposé par react-router dans v4, seulement dans v2.
Au lieu de cela, vous avez quelques options pour faire ceci:
-
Utilisez le composant
withRouter
d'ordre ÉlevéÀ la place, vous devez utiliser le composant
withRouter
d'ordre élevé, et l'envelopper dans le composant qui va pousser à 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);
Découvrez la la documentation officielle pour plus d'infos:
, Vous pouvez obtenir l'accès à la
history
propriétés de l'objet et les plus proches<Route>
'smatch
via le composant d'ordre supérieur withRouter. withRouter restituera son composant chaque fois que la route change avec les mêmes accessoires que<Route>
rendre les accessoires:{ match, location, history }
.
-
Utilisez L'API
context
L'Utilisation du contexte peut être l'une des solutions les plus faciles, mais étant une API expérimentale, elle est instable et non prise en charge. Utiliser seulement quand 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"); } ... }
Jetez un oeil à l'la documentation officielle le contexte:
Si vous voulez que votre application soit stable, n'utilisez pas context. C'est une API expérimentale et il est susceptible de casser dans les futures versions de React.
Si vous insistez pour utiliser context malgré ces avertissements, essayez d'isoler votre utilisation de context dans une petite zone et évitez d'utiliser l'API context directement si possible afin qu'il soit plus facile de mettre à 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 panier, il sera enregistré dans l'objet historique.
Profiter, Michael.
Selon Réagir Routeur v4 documentation - Redux l'Intégration en Profondeur de la session
Une intégration profonde est nécessaire pour:
"Être capable de naviguer en répartissant des actions"
Cependant, ils recommandent cette approche comme alternative à l '"intégration profonde":
" plutôt que de répartir les actions pour naviguer, vous pouvez passer l'objet historique fourni pour acheminer les composants vers vos actions et naviguer avec lui."
Donc vous peut envelopper votre composant avec le composant withRouter high order:
export default withRouter(connect(null, { actionCreatorName })(ReactComponent));
Qui passera L'API d'historique aux accessoires. Ainsi, vous pouvez appeler le créateur d'action en passant l'historique en tant que paramètre. Par exemple, dans votre ReactComponent:
onClick={() => {
this.props.actionCreatorName(
this.props.history,
otherParams
);
}}
Ensuite, à l'intérieur de vos actions / index.js:
export function actionCreatorName(history, param) {
return dispatch => {
dispatch({
type: SOME_ACTION,
payload: param.data
});
history.push("/path");
};
}
Question méchante, m'a pris beaucoup de temps, mais finalement, je l'ai résolu de cette façon:
Enveloppez votre conteneur avec withRouter
et passez l'historique à votre action dans la fonction mapDispatchToProps
. En action utiliser l'historique.appuyez sur ('/url') pour naviguer.
Action:
export function saveData(history, data) {
fetch.post('/save', data)
.then((response) => {
...
history.push('/url');
})
};
Conteneur:
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 les Réagir Routeur v4.x .
this.context.history.push
ne fonctionnera pas.
J'ai réussi à faire fonctionner push comme ceci:
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 passer 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 de plus au cas où cela vaudrait la peine pour quelqu'un d'autre.
J'ai un fichier history.js
où j'ai ce qui suit:
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 ce qui suit:
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 j'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 les appels D'API.
J'espère que ça aide!
Si vous utilisez Redux, je vous recommande d'utiliser le paquet npm react-router-redux . Il vous permet d'envoyer des actions de navigation de magasin Redux.
Vous devez créer un magasin comme décrit dans leur 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:
Conteneur:
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
);
}
}
J'ai pu accomplir ceci en utilisant bind()
. Je voulais cliquer sur un bouton dans index.jsx
, poster des données sur le serveur, évaluer la réponse et rediriger vers success.jsx
. Voici comment j'ai travaillé que sur...
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": "test@test.com"
}
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
, Donc par la liaison this
à postData
dans index.jsx
, j'ai été en mesure d'accéder this.props.history
dans request.js
... ensuite, je peux réutiliser cette fonction dans différents composants, juste pour m'assurer que je me souviens d'inclure this.postData = postData.bind(this)
dans le constructor()
.
Utiliser Le Rappel. Il a travaillé 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();
});
}
Dans le composant, il vous suffit d'ajouter le rappel
this.props.addProduct(props, () => this.props.history.push('/cart'))
Voici mon hack (c'est mon fichier au niveau racine, avec un peu de redux mélangé là-bas-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()
où je veux (par exemple, dans mes fonctions de magasin redux/thunks/sagas, etc) j'avais espéré pouvoir utiliser window.customHistory.push()
mais pour une raison quelconque react-router
n'a jamais semblé mettre à jour même si l'url a changé. Mais de cette façon, j'ai L'instance exacte react-router
utilise. Je n'aime pas mettre des choses dans la portée mondiale, 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 IMO.
Vous pouvez l'utiliser comme ceci comme je le fais pour login 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>
)
/*Step 1*/
myFunction(){ this.props.history.push("/home"); }
/**/
<button onClick={()=>this.myFunction()} className={'btn btn-primary'}>Go
Home</button>
Première étape enveloppez 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 la Route et puis passe ces accessoires. Probablement dans un de vos fichiers.
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 la console.journal(ce.props) dans mon fichier JS composant que je devrais obtenir quelque chose qui ressemble à ceci
{match: {…}, location: {…}, history: {…}, //other stuff }
Étape 2 je peux accéder à l'objet historique pour changer mon 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 codage bootcamp, donc je ne suis pas un expert, mais je sais vous pouvez également utiliser
window.location = "/" //wherever you want to go
Corrigez-moi si je me trompe, mais quand j'ai testé cela, il a rechargé toute la page que je pensais avoir vaincue tout le point d'utiliser React.