React Router v4で履歴をプッシュする方法は?


361

現在のバージョンのReact Router(v3)では、サーバーの応答を受け入れ、を使用browserHistory.pushして適切な応答ページに移動できます。ただし、これはv4では使用できません。また、これを処理する適切な方法が何かはわかりません。

この例では、Reduxを使用して、ユーザーがフォームを送信すると、components / app-product-form.jsが呼び出さthis.props.addProduct(props)れます。サーバーが成功を返すと、ユーザーはカートページに移動します。

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

React Router v4の機能からカートページにリダイレクトするにはどうすればよいですか?


提供された最後のソリューションから、およびGitHub上のReact Routerの問題の提案からこれに追加するために、context手動で必要なものを渡すためにを使用することは「ノーゴー」です。私が図書館の著者でない限り、それを使用する必要はありません。実際、Facebookはそれを推奨していません。
Chris

@クリスはこれの解決策を見つけましたか?ここで説明したのと同じように、動作中の別のコンポーネントにプッシュする必要があります
Mr.G

@Gさん、悲しいことに私はしていません。最後に読んだのは、React Routerが利用可能なreduxパッケージを維持しているReactトレーニングチームです。私はそれを機能させるために運がありませんでした、そして彼らはそれを解決するために多くの仕事をしていません。
Chris

windows.location.href = URLを使用できないのはなぜですか?それを使用してURLを変更してリダイレクトすることに問題はありますか?
Shan

理由はわかりませんが、使用するためのオプションhistoryはReact Nativeでもオプションとして機能し、レガシーブラウザーをサポートする追加オプションとしても機能します。
Chris

回答:


308

historyコンポーネントの外部でメソッドを使用できます。以下の方法でお試しください。

まず、履歴パッケージをhistory使用するオブジェクトを作成します。

// src/history.js

import { createBrowserHistory } from 'history';

export default createBrowserHistory();

次に、それをラップします<Router>注意してくださいimport { Router }代わりに使用する必要があります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'),
);

現在の場所を任意の場所から変更します。次に例を示します。

// src/actions/userActionCreators.js

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

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

UPDReact Router FAQで少し異なる例を見ることができます。


24
@OlegBelostotskyの発言とまったく同じようにしようとしましたが、後history.push('some path')にURLは変更されますが、ページは変更されません。私はwindow.location.reload()それを機能させるために、コードのいくつかの部分にそれを付け加えなければなりません。ただし、1つのインスタンスではredux状態ツリーを保持する必要があり、リロードするとそれが破棄されます。他の解決策はありますか?
sdabrutas

2
@idunno withRouter高次コンポーネントを使用してみてください。
Oleg Belostotsky

これにより、次のエラーが表示されました。createBrowserHistoryは関数ではありません。私に何ができる?
AKJ 2018年

@AKJ多分import createHistory from 'history/createBrowserHistory'; export default createHistory();仕事になります。
Oleg Belostotsky

1
反対投票して申し訳ありません:)。これも機能するはずですが、これを処理する正しい方法は、Chrisの回答であるstackoverflow.com/a/42716055/491075です。
gion_13

340

React Router v4はv3(およびそれ以前)とは根本的に異なり、以前のbrowserHistory.push()ようにはできません。

詳細については、このディスカッションが関連しているようです。

  • 独自の履歴インスタンスを作成し、その変更をリッスンするbrowserHistoryため、新規作成は機能しません<BrowserRouter>。したがって、別のインスタンスはURLを変更しますが、を更新しません<BrowserRouter>
  • browserHistory v4ではreact-routerによって公開されず、v2でのみ公開されます。

代わりに、これを行うにはいくつかのオプションがあります。

  • withRouter高次コンポーネントを使用する

    代わりに、withRouter高次コンポーネントを使用して、履歴にプッシュするコンポーネントにラップする必要があります。例えば:

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

    詳細については、公式ドキュメントをご覧ください。

    あなたはへのアクセスを得ることができhistory、オブジェクトのプロパティと最も近い<Route>のをmatchwithRouter高次のコンポーネントを介して。withRouterは、<Route>レンダープロップと同じプロップでルートが変更されるたびに、コンポーネントを再レンダリングします{ match, location, history }


  • contextAPIを使用する

    コンテキストの使用は最も簡単な解決策の1つかもしれませんが、実験的なAPIであるため、不安定でサポートされていません。他のすべてが失敗した場合にのみ使用してください。次に例を示します。

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

    見てい公式ドキュメント文脈上を:

    アプリケーションを安定させたい場合は、コンテキストを使用しないでください。これは実験的なAPIであり、Reactの将来のリリースで機能しなくなる可能性があります。

    これらの警告にもかかわらずコンテキストの使用を主張する場合は、コンテキストの使用を小さな領域に分離し、APIが変更されたときにアップグレードしやすくするために、可能な場合はコンテキストAPIを直接使用しないようにしてください。


9
はい、試しました。質問してくれてありがとう。:-)では、どのようにしてこのアクション関数のコンテキストを取得しますか?これまでのところ、それは未定義として登場しています。
Chris

1
私はこのトピックを数日間研究しており、これを機能させることができませんでした。上記の例を使用しても、コンテキスト内でルーターが定義されていません。私は現在、react v15.5.10、react-router-dom v4.1.1、prop-types 15.5.10を使用しています。これに関するドキュメントはまばらで、あまり明確ではありません。
Stu

5
@Stuこれは機能するはずですthis.context.router.history.push('/path');
Tushar Khatiwada 2017年

1
@Chris確かに、履歴オブジェクトをインスタンス化して自分で使用しようとすると、URLは変更されますが、コンポーネントはレンダリングされません。しかし、コンポーネントの外で「history.push」を使用し、コンポーネントを強制的にレンダリングする方法は?
クール

52
これは、コンポーネントのhistory.push OUTSIDEにアクセスする方法についての質問には答えません。withRouterやコンテキストを使用することは、コンポーネントの外ではオプションではありません。
SunshinyDoyle 2017年

49

これで、react-router v5を使用すると、次のようにuseHistoryライフサイクルフックを使用できます。

import { useHistory } from "react-router-dom";

function HomeButton() {
  let history = useHistory();

  function handleClick() {
    history.push("/home");
  }

  return (
    <button type="button" onClick={handleClick}>
      Go home
    </button>
  );
}

続きを読む:https : //reacttraining.com/react-router/web/api/Hooks/usehistory


1
それは非常に歓迎されるアップデートです!ありがとう!
Chris

これを設定するために必要な特定の方法はありますか?次のように呼び出しますlet history = useHistory();Object is not callable、useHistoryがどのconsole.log(useHistory)ように定義されていないのかを確認しようとすると、エラーが発生します。使用"react-router-dom": "^5.0.1"
steff_bdh

@steff_bdh yout package.jsonファイルで「react-router-dom」に更新する必要があります:「^ 5.0.1」と実行して「npm install」
Hadi Abu

2
いいですが、reuxコンポーネント/関数ではないため、reuxアクションクラス内でフックを使用できません
Jay

(async)を使用してログイン時にリダイレクトするためにこれをどのように使用しますか ここでの質問は=>ですstackoverflow.com/questions/62154408/...
theairbend3r

29

React Router 4で最も簡単な方法は、

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

ただし、このメソッドを使用するには、既存のコンポーネントがhistoryオブジェクトにアクセスできる必要があります。アクセスできます

  1. コンポーネントがRoute直接リンクされている場合、コンポーネントはすでにhistoryオブジェクトにアクセスできます。

    例えば:

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

    ここからViewProfileにアクセスできますhistory

  2. Route直接接続されていない場合。

    例えば:

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

    次にwithRouter、既存のコンポーネントを歪めるために、より重い注文関数を使用する必要があります。

    内部 ViewUsersコンポーネント

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

    • export default withRouter(ViewUsers);

    これで完了です。ViewUsersコンポーネントはhistoryオブジェクトにアクセスできます。

更新

2-このシナリオでは、すべてのルートpropsをコンポーネントに渡すとthis.props.history、コンポーネントがなくてもコンポーネントからアクセスできますHOC

例えば:

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

1
優秀な!私のコンポーネント(へのアクセスが必要this.props.history)はHOCから来ているため、2番目の方法でもうまくRouteいきました。
cjauvin

25

これは私がそれをした方法です:

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

this.props.history.push('/cart');カートページにリダイレクトするために使用します。履歴オブジェクトに保存されます。

楽しんで、マイケル。


2
はい、コンポーネント内でうまくプッシュできるように見えます。コンポーネント外への移動に影響を与える唯一の方法は、リダイレクトを使用することです。
クリス

14
これは、コンポーネントのhistory.push OUTSIDEにアクセスする方法についての質問には答えません。this.props.historyの使用は、コンポーネントの外部にある場合のオプションではありません。
SunshinyDoyle 2017年

22

React Router v4のドキュメントによると-Redux Deep Integrationセッション

深い統合が必要です:

「ディスパッチアクションでナビゲートできる」

ただし、「深い統合」の代わりにこのアプローチを推奨しています。

「ナビゲートするアクションをディスパッチするのではなく、提供された履歴オブジェクトを渡して、コンポーネントをアクションにルーティングし、そこでナビゲートすることができます。」

したがって、withRouter高次コンポーネントでコンポーネントをラップできます。

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

これは、履歴APIを小道具に渡します。したがって、履歴をパラメータとして渡してアクション作成者を呼び出すことができます。たとえば、ReactComponent内で:

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

次に、actions / index.js内で:

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

19

厄介な質問ですが、かなりの時間がかかりましたが、最終的には次のように解決しました。

コンテナーをラップしてwithRoutermapDispatchToProps関数のアクションに履歴を渡します 。実際には、history.push( '/ url')を使用して移動します。

アクション:

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

容器:

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

これは、React Router v4.xで有効です。


おかげで、あなたのwithRouterソリューションはtypescriptで動作しますが、以前のimport { createBrowserHistory } from 'history' アイデアと比較してかなり遅いですか?
ジェイ

7

this.context.history.push 動作しないでしょう。

私はなんとかこのようにプッシュを動作させることができました:

static contextTypes = {
    router: PropTypes.object
}

handleSubmit(e) {
    e.preventDefault();

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

}

9
これは、コンポーネントのhistory.push OUTSIDEにアクセスする方法についての質問には答えません。this.contextを使用することは、コンポーネントの外部にある場合のオプションではありません。
SunshinyDoyle 2017年

6

この場合、小道具をサンクに渡します。だからあなたは単に呼び出すことができます

props.history.push('/cart')

そうでない場合でも、コンポーネントから履歴を渡すことができます

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

6

他の誰かにとって価値がある場合に備えて、もう1つの解決策を提供します。

私は次のhistory.jsファイルを持っています:

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

次に、ルーターを定義するルートで、以下を使用します。

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

最後に、私actions.jsは履歴をインポートしてpushLaterを利用します

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

このようにして、API呼び出しの後に新しいアクションにプッシュできます。

それが役に立てば幸い!


4

Reduxを使用している場合は、npmパッケージreact-router-reduxの使用をお勧めします。Reduxストアのナビゲーションアクションをディスパッチできます。

Readmeファイルの説明に従ってストアを作成する必要があります

最も簡単な使用例:

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

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

コンテナ/コンポーネントの2番目の使用例:

容器:

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

成分:

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

1
これは、現在開発中のバージョンを使用している場合を除き、react-router-redux では機能しませnext
froginvasion 2017年

4

私はこれを使ってこれを達成することができましたbind()。のボタンをクリックindex.jsxし、サーバーにデータを投稿し、応答を評価して、にリダイレクトしたいと思いましたsuccess.jsx。ここで私はそれをどのように解決したかです...

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

だから、結合することによってthispostDataindex.jsx、私はアクセスすることができたthis.props.historyrequest.js...その後、私はちょうど私が必ず含まれるように覚えて確認する必要があり、異なるコンポーネントでこの機能を再利用することができますthis.postData = postData.bind(this)constructor()


3

ここに私のハックがあります(これは私のルートレベルのファイルであり、そこに小さなreduxが混在しています-私は使用していません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')
)

その後、好きなwindow.appHistory.push()場所で使用できます(たとえば、reduxストアの関数/サンク/サガなど)。使用したいと思っていましたが、URLが変更されても、window.customHistory.push()何らかの理由でreact-router更新されないようでした。しかし、この方法では、EXACTインスタンスをreact-router使用しています。私はものをグローバルスコープに入れるのが好きではありません、そしてこれは私がそれでそれをする数少ないものの一つです。しかし、それは私がIMOを見た他のどの代替案よりも優れています。


3

コールバックを使用します。それは私のために働いた!

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

コンポーネントでは、コールバックを追加するだけです

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

3

私はそれを行う方法は、次のとおりです。 -代わりに使用してリダイレクトするhistory.push、私はちょうど使用Redirectからコンポーネントreact-router-dom あなただけ渡すことができ、このコンポーネントを使用する場合push=true、それは残りの世話をします

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

これは正解で、反応レンダリングサイクルを中断しません
jzqa

1

ReactルーターV4では、履歴プロップを以下のように使用できるようになりました。

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

値は、state:{value}コンポーネントの状態ではなく、場所のプロパティが利用可能な場所であればどこからでもアクセスできます 。


1
これは、コンポーネントのhistory.push OUTSIDEにアクセスする方法についての質問には答えません。this.props.historyの使用は、コンポーネントの外部にある場合のオプションではありません。
アルカディ

0

このように使用して、ログインやさまざまなことを行うことができます

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
/*Step 1*/
myFunction(){  this.props.history.push("/home"); }
/**/
 <button onClick={()=>this.myFunction()} className={'btn btn-primary'}>Go 
 Home</button>

インポートは必要ありません!
David Bagdasaryan 2018年

1
このコードは質問に答えることがありますが、このコードが質問に答える理由や方法に関する追加のコンテキストを提供すると、長期的な価値が向上します。
rollstuhlfahrer 2018年

0

ステップ1アプリをルーターでラップする

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

これで、私のアプリ全体がBrowserRouterにアクセスできるようになります。ステップ2ルートをインポートしてから、それらの小道具を渡します。おそらくメインファイルの1つにあります。

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

ここで、コンポーネントのjsファイルでconsole.log(this.props)を実行すると、次のようになります。

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

ステップ2履歴オブジェクトにアクセスして場所を変更できます

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

また、私はコーディングブートキャンプの学生なので、私は専門家ではありませんが、あなたも使用できることを知っています

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

私が間違っている場合は修正してください。しかし、それをテストしたところ、ページ全体がリロードされ、Reactを使用する全体のポイントを打ち負かしたと思いました。


0

Router独自のカスタムを作成します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

次に、を定義するルートでRouter、以下を使用します。

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

最後に、history必要な場所にインポートして使用します。

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

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

0

コンポーネントのプロップに値として関数を渡すときに履歴を使用したい場合、react-router 4history使用すると、<Route/>コンポーネントのレンダー属性でプロップを単純に分解してから使用できます。history.push()

    <Route path='/create' render={({history}) => (
      <YourComponent
        YourProp={() => {
          this.YourClassMethod()
          history.push('/')
        }}>
      </YourComponent>
    )} />

注:これを機能させるには、React RouterのBrowserRouterコンポーネントをルートコンポーネント(たとえば、index.jsにある可能性があります)でラップする必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.