web-dev-qa-db-ja.com

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

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の機能から[Cart]ページにリダイレクトする方法を教えてください。

242
Chris

あなたのコンポーネントの外で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 には、少し異なる例もあります。

143

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

この議論 さらに情報が必要な場合は/に関連があるようです:

  • <BrowserRouter>は独自の履歴インスタンスを作成し、それに対する変更をリッスンするため、新しいbrowserHistoryを作成してもうまくいきません。したがって、別のインスタンスがURLを変更しても<BrowserRouter>は更新されません。
  • v4ではbrowserHistoryは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>match にアクセスするには、withRouter高次コンポーネントを使います。 withRouterは、ルートが <Route> render props:{ match, location, history }と同じプロップで変更されるたびにそのコンポーネントを再レンダリングします。


  • context APIを使用する

    コンテキストを使用するのが最も簡単な解決策の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を直接使用しないようにしてください。

252
Chris

これが私のやり方です。

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');を使用します。

マイケル、お楽しみください。

21

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

厄介な質問ですが、かなり時間がかかりましたが、結局、私はこのようにして解決しました。

あなたのコンテナをwithRouterでラップし、mapDispatchToProps関数であなたのアクションに履歴を渡します。実際には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に有効です

15

this.context.history.Pushは機能しません。

私はどうにかしてPushを次のように動作させました。

static contextTypes = {
    router: PropTypes.object
}

handleSubmit(e) {
    e.preventDefault();

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

}
6
Aimar

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

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')
    })
  }
}
5
user3812377

React Router 4の最も簡単な方法は使用することです 

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

しかし、このメソッドを使うためには、あなたの existing コンポーネントはhistoryオブジェクトにアクセスできなければなりません。アクセス可能

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

    例えば:

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

    ここでViewProfilehistoryにアクセスできます。

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

    例えば:

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

    それから既存のコンポーネントをワープするために、より高次の関数であるwithRouterを使わなければなりません。

    内側 ViewUsersコンポーネント 

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

    • export default withRouter(ViewUsers);

    これで今、あなたのViewUsersコンポーネントはhistoryオブジェクトにアクセスできます。

_アップデート_

2-このシナリオでは、すべてのルートpropsをあなたのコンポーネントに渡してください。そうすればHOCがなくてもコンポーネントからthis.props.historyにアクセスできます。

例えば:

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

他の人にとって価値がある場合に備えて、もう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にHistoryをインポートしてpushLaterを利用します

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

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

それが役に立てば幸い!

4
Diego

ここに私のハックがあります(これは私のルートレベルのファイルで、そこに小さな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ストアの関数/サンク/サガなど)で使用できます。ただwindow.customHistory.Push()を使用できることを望んでいましたが、何らかの理由で、react-routerはURLが変更されても更新されないようでした。しかし、このようにreact-routerが使用するEXACTインスタンスがあります。グローバルな範囲に物を置くのは好きではありませんが、これは私がそれを行う数少ないものの1つです。しかし、それは私がIMOを見た他のどの選択肢よりも優れています。

3
Joao

コールバックを使用してください。それは私のために働いた!

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

もしあなたがReduxを使っているなら、npm package react-router-redux を使うことをお勧めします。それはあなたがReduxストアナビゲーションアクションをディスパッチすることを可能にします。

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

最も簡単なユースケース:

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

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

Container/Componentの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
    );
  }
}
2
Black

私は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": "[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

index.jsxthispostDataにバインドすることで、this.props.history...でrequest.jsにアクセスすることができました。それから私はthis.postData = postData.bind(this)constructor()を含めることを忘れないでください。

2
skwidbreth

React router V4では、history propを以下のように使うことができます。

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

この値は、ロケーションプロップが使用可能な場合はいつでも、コンポーネント状態ではなく state:{value}としてアクセスできます。

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

history.Pushを使用してリダイレクトする代わりに、react-router-domからRedirectコンポーネントを使用するだけです。このコンポーネントを使用するときは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}/>
  }
}
0
LuizAsFight

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

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

これで私のアプリ全体がBrowserRouterにアクセスできるようになります。ステップ2私はRouteをインポートしてからそれらの小道具を渡します。おそらくあなたのメインファイルの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
user2785628

独自の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
Nolesh