web-dev-qa-db-de.com

Wie man den Verlauf in React Router v4 durchführt?

In der aktuellen Version von React Router (v3) kann ich eine Serverantwort akzeptieren und mit browserHistory.Push zur entsprechenden Antwortseite wechseln. Dies ist jedoch nicht in Version 4 verfügbar, und ich bin mir nicht sicher, wie dies richtig zu handhaben ist.

In diesem Beispiel ruft components/app-product-form.js mit Redux this.props.addProduct(props) auf, wenn ein Benutzer das Formular sendet. Wenn der Server erfolgreich war, wird der Benutzer zur Seite "Warenkorb" geleitet.

// 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
      });
}

Wie kann ich von der Funktion für React Router v4 eine Weiterleitung zur Warenkorb-Seite vornehmen?

211
Chris

Sie können die history-Methoden außerhalb Ihrer Komponenten verwenden. Versuchen Sie es auf folgende Weise.

Erstellen Sie zuerst ein history-Objekt, das verwendet wird das Protokollpaket :

// src/history.js

import { createBrowserHistory } from 'history';

export default createBrowserHistory();

Dann wickeln Sie es in <Router> ein (bitte beachten Sie, Sie sollten statt import { Router }import { BrowserRouter as Router } verwenden)

// 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'),
);

Ändern Sie Ihren aktuellen Standort von einem beliebigen Ort aus, zum Beispiel:

// src/actions/userActionCreators.js

// ...
import history from '../history';

export function login(credentials) {
  return function (dispatch) {
    return loginRemotely(credentials)
      .then((response) => {
        // ...
        history.Push('/');
      });
  };
}

UPD: Sie können auch ein etwas anderes Beispiel in React Router FAQ sehen.

143

React Router v4 unterscheidet sich grundlegend von Version 3 (und früher) und Sie können browserHistory.Push() nicht wie gewohnt ausführen.

Diese Diskussion scheint verwandt zu sein, wenn Sie weitere Informationen wünschen:

  • Das Erstellen einer neuen browserHistory funktioniert nicht, da <BrowserRouter> eine eigene Verlaufsinstanz erstellt und auf Änderungen daran wartet. Eine andere Instanz ändert also die URL, aktualisiert jedoch nicht den <BrowserRouter>.
  • browserHistory wird von Reakt-Router in Version 4 nicht angezeigt, nur in Version 2.

Stattdessen haben Sie einige Möglichkeiten, dies zu tun:

  • Verwenden Sie die withRouter-Komponente hoher Ordnung

    Verwenden Sie stattdessen die Komponente withRouter hoher Ordnung, und wickeln Sie diese Komponente in die Komponente ein, die Push to history verwendet. Zum Beispiel:

    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);
    

    In der offiziellen Dokumentation finden Sie weitere Informationen:

    Auf die Eigenschaften des Objekts history / und den nächstgelegenen <Route> 's match können Sie über die Komponente withRouter höherer Ordnung zugreifen. withRouter rendert seine Komponente jedes Mal neu, wenn die Route mit den gleichen Requisiten wie <Route> render props geändert wird: { match, location, history }.


  • Verwenden Sie die context-API

    Die Verwendung des Kontexts ist möglicherweise eine der einfachsten Lösungen. Als experimentelle API ist sie jedoch instabil und wird nicht unterstützt. Verwenden Sie es nur, wenn alles andere fehlschlägt. Hier ist ein Beispiel: 

    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");
      }
      ...
    }
    

    Schauen Sie sich die offizielle Dokumentation on context an:

    Wenn Sie möchten, dass Ihre Anwendung stabil ist, verwenden Sie keinen Kontext. Es handelt sich um eine experimentelle API, die wahrscheinlich in zukünftige Versionen von React einbricht.

    Wenn Sie trotz dieser Warnungen auf der Verwendung von Kontext bestehen, versuchen Sie, Ihre Kontextverwendung in einem kleinen Bereich zu isolieren, und vermeiden Sie es, die Kontext-API direkt zu verwenden, wenn dies möglich ist, sodass das Upgrade bei Änderungen der API einfacher ist.

252
Chris

So habe ich es gemacht:

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>
        );
    }
}

Verwenden Sie this.props.history.Push('/cart');, um zur Warenkorb-Seite umzuleiten. Diese wird im Verlaufsobjekt gespeichert.

Viel Spaß, Michael.

21

Gemäß Dokumentation zu React Router v4 - Redux Deep Integration-Sitzung

Eine tiefe Integration ist erforderlich, um:

"in der Lage sein zu navigieren, indem Aktionen ausgelöst werden"

Sie empfehlen diesen Ansatz jedoch als Alternative zur "tiefen Integration":

"Anstatt Aktionen zur Navigation auszulösen, können Sie das bereitgestellte Verlaufsobjekt zum Weiterleiten von Komponenten an Ihre Aktionen übergeben und dort damit navigieren."

So können Sie Ihre Komponente mit der übergeordneten withRouter-Komponente umhüllen:

export default withRouter(connect(null, { actionCreatorName })(ReactComponent));

das wird die History-API an Requisiten weitergeben. Sie können also den Aktionsersteller aufrufen, der den Verlauf als Parameter übergibt. Zum Beispiel in Ihrer ReactComponent:

onClick={() => {
  this.props.actionCreatorName(
    this.props.history,
    otherParams
  );
}}

Dann in Ihrer Datei actions/index.js:

export function actionCreatorName(history, param) {
  return dispatch => {
    dispatch({
      type: SOME_ACTION,
      payload: param.data
    });
    history.Push("/path");
  };
}
20
alainbex

Am einfachsten ist es in React Router 4 zu benutzen

this.props.history.Push('/new/url');

Um diese Methode zu verwenden, sollte Ihre vorhandene Komponente Zugriff auf das Objekt history haben. Wir können den Zugang durch bekommen

  1. Wenn Ihre Komponente direkt mit Route verknüpft ist, hat Ihre Komponente bereits Zugriff auf das Objekt history.

    z.B:

    <Route path="/profile" component={ViewProfile}/>
    

    Hier hat ViewProfile Zugriff auf history.

  2. Wenn nicht direkt mit Route verbunden.

    z.B:

    <Route path="/users" render={() => <ViewUsers/>}
    

    Dann müssen wir withRouter verwenden, eine Funktion höherer Ordnung, um die vorhandene Komponente zu verzerren.

    InnerhalbViewUsers Komponente

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

    • export default withRouter(ViewUsers);

    Das war's jetzt, Ihre ViewUsers Komponente hat Zugriff auf das history Objekt.

UPDATE

2 - Übergeben Sie in diesem Szenario alle Routen props an Ihre Komponente, und dann können wir von der Komponente aus auch ohne HOC auf this.props.history Zugreifen.

z.B:

<Route path="/users" render={props => <ViewUsers {...props} />}

Nasty Frage, hat mich ziemlich viel Zeit gekostet, aber irgendwann habe ich es so gelöst:

Wickeln Sie Ihren Container mit withRouter und übergeben Sie den Verlauf in mapDispatchToProps an Ihre Aktion. Verwenden Sie in Aktion history.Push ('/ url') zum Navigieren. 

Aktion:

export function saveData(history, data) {
  fetch.post('/save', data)
     .then((response) => {
       ...
       history.Push('/url');
     })
};

Container: 

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));

Dies gilt für React Router v4.x .

15

this.context.history.Push wird nicht funktionieren.

Ich habe es geschafft, dass Push so funktioniert:

static contextTypes = {
    router: PropTypes.object
}

handleSubmit(e) {
    e.preventDefault();

    if (this.props.auth.success) {
        this.context.router.history.Push("/some/Path")
    }

}
6
Aimar

In diesem Fall übergeben Sie dem Thunk Requisiten. Sie können also einfach anrufen

props.history.Push('/cart')

Ist dies nicht der Fall, können Sie immer noch den Verlauf Ihrer Komponente übergeben 

export function addProduct(data, history) {
  return dispatch => {
    axios.post('/url', data).then((response) => {
      dispatch({ type: types.AUTH_USER })
      history.Push('/cart')
    })
  }
}
5
user3812377

Ich biete eine weitere Lösung an, falls es für jemand anderen wert ist.

Ich habe eine history.js-Datei, in der ich Folgendes habe:

import createHistory from 'history/createBrowserHistory'
const history = createHistory()
history.pushLater = (...args) => setImmediate(() => history.Push(...args))
export default history

Als nächstes verwende ich in meinem Stamm, in dem ich meinen Router definiere, Folgendes:

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>
    )
   }
  }

Schließlich importiere ich auf meinem actions.js History und benutze pushLater

import history from './history'
export const login = createAction(
...
history.pushLater({ pathname: PATH_REDIRECT_LOGIN })
...)

Auf diese Weise kann ich nach API-Aufrufen neue Aktionen durchführen.

Ich hoffe es hilft!

4
Diego

Dies konnte ich mit bind() erreichen. Ich wollte auf eine Schaltfläche in index.jsx Klicken, einige Daten an den Server senden, die Antwort auswerten und zu success.jsx Umleiten. So habe ich das herausgefunden ...

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

Durch das Binden von this an postData in index.jsx Konnte ich auf this.props.history In request.js Zugreifen. Dann kann ich dieses wiederverwenden Funktion in verschiedenen Komponenten, muss nur sicherstellen, dass ich daran denke, this.postData = postData.bind(this) in die constructor() aufzunehmen.

4
skwidbreth

Rückruf verwenden. Es hat für mich funktioniert!

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();
  });
}

In der Komponente müssen Sie nur den Rückruf hinzufügen

this.props.addProduct(props, () => this.props.history.Push('/cart'))
3

Wenn Sie Redux verwenden, würde ich empfehlen, das npm-Paket React-Router-Redux zu verwenden. Sie können Redux-Store-Navigationsaktionen auslösen.

Sie müssen store wie in der Readme-Datei beschrieben erstellen.

Der einfachste Anwendungsfall:

import { Push } from 'react-router-redux'

this.props.dispatch(Push('/second page'));

Zweiter Anwendungsfall mit Container/Component:

Container:

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);

Komponente:

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
    );
  }
}
2
Black

Der React-Router V4 ermöglicht jetzt die Verwendung der Verlaufsstütze wie folgt:

this.props.history.Push("/dummy",value)

Auf den Wert kann dann zugegriffen werden, wenn die Standort-Eigenschaft als state:{value} nicht als Komponentenzustand verfügbar ist.

1
Snivio

Hier ist mein Hack (dies ist meine Root-Level-Datei mit ein wenig Redux darin - obwohl ich nicht react-router-redux benutze):

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')
)

Ich kann dann window.appHistory.Push() überall verwenden, wo ich möchte (z. B. in meinen Redux-Store-Funktionen/thunks/sagas usw.). Ich hatte gehofft, ich könnte window.customHistory.Push() verwenden, aber aus irgendeinem Grund schien react-router nie zu aktualisieren, obwohl sich die URL geändert hat. Aber so habe ich die EXACT-Instanz react-router. Ich mag es nicht, Sachen in den globalen Rahmen zu stellen, und dies ist eines der wenigen Dinge, mit denen ich das tun würde. Aber es ist besser als jede andere Alternative, die ich IMO gesehen habe.

1
Joao
/*Step 1*/
myFunction(){  this.props.history.Push("/home"); }
/**/
 <button onClick={()=>this.myFunction()} className={'btn btn-primary'}>Go 
 Home</button>
0

sie können es so verwenden, da ich es für die Anmeldung und viele andere Dinge mache 

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>

)
0
jadlmir

so mache ich das: - Anstatt mit history.Push umzuleiten, verwende ich nur die Komponente Redirect aus react-router-dom. Wenn Sie diese Komponente verwenden, können Sie einfach Push=true übergeben, und der Rest wird erledigt

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}/>
  }
}
0
LuizAsFight

schritt eins wickeln Sie Ihre App in den Router ein

import { BrowserRouter as Router } from "react-router-dom";
ReactDOM.render(<Router><App /></Router>, document.getElementById('root'));

Jetzt hat meine gesamte App Zugriff auf BrowserRouter. Schritt zwei: Ich importiere Route und übergebe diese Requisiten. Wahrscheinlich in einer Ihrer Hauptdateien. 

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>
      )}
    />

Wenn ich nun console.log (this.props) in meiner Komponentendatei js ausführe, sollte ich etwas bekommen, das so aussieht

{match: {…}, location: {…}, history: {…}, //other stuff }

Schritt 2 Ich kann auf das Verlaufsobjekt zugreifen, um meinen Standort zu ändern

//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

Ich bin auch nur ein Programmierer für das Bootcamp, also bin ich kein Experte, aber ich weiß, dass Sie auch Sie verwenden können 

window.location = "/" //wherever you want to go

Korrigieren Sie mich, wenn ich falsch liege, aber als ich das ausprobiert habe, wurde die gesamte Seite neu geladen. 

0
user2785628

Erstellen Sie ein benutzerdefiniertes Router mit einem eigenen 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

Verwenden Sie als Nächstes in Ihrem Stammverzeichnis, in dem Sie Router definieren, Folgendes:

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>
    )
  }
}

Importieren Sie schließlich history, wo Sie es benötigen, und verwenden Sie es:

import { history } from '../routers/ExtBrowserRouter';
...

export function logout(){
  clearTokens();      
  history.Push('/login'); //WORKS AS EXPECTED!
  return Promise.reject('Refresh token has expired');
}
0
Nolesh