2016-10-06 28 views
23

我迫不及待地跳上了最新的alpha版本react-router v4。全新<BrowserRouter/>在保持用戶界面與瀏覽器歷史記錄同步方面非常出色,但如何使用它來以編程方式導航?在React-Router v4中以編程方式導航

+0

spik3s:注意,接受的答案沒有工作,但是這一個是:https://stackoverflow.com/a/43250939/1533892 - 可能有助於讀者接受這一個。免責聲明:工作答案是我的:) –

+0

@HarlanTWood謝謝!沒有時間更新我的答案,感謝您的支持。已經將您的答案標記爲正確。 – spik3s

+0

非常好,謝謝:) –

回答

22

路由器將推動一個history對象到props散列中的組件。因此,在你的組件,簡單地做:

this.props.history.push('/mypath')

這是一個完整的例子:

App.js

import React from 'react' 
import {BrowserRouter as Router, Route} from 'react-router-dom' 

import Login from './Login' 

export default class App extends React.Component { 
    render() { 
    return (
     <Router> 
     <div> 
      <Route exact path='/login' component={Login} /> 
     </div> 
     </Router> 
    ) 
    } 
} 

Login.js

import React, {PropTypes} from 'react' 

export default class Login extends React.Component { 
    constructor(props) { 
    super(props) 
    this.handleLogin = this.handleLogin.bind(this) 
    } 

    handleLogin(event) { 
    event.preventDefault() 
    // do some login logic here, and if successful: 
    this.props.history.push(`/mypath`) 
    } 

    render() { 
    return (
     <div> 
     <form onSubmit={this.handleLogin}> 
      <input type='submit' value='Login' /> 
     </form> 
     </div> 
    ) 
    } 
} 
+1

woow謝謝。這節省了我的一天。完美地與最新版本的反應和反應路由器 –

+1

投擲錯誤'無法讀取未定義的屬性'對象,任何破解? –

+0

@PardeepJain你可以包括堆棧跟蹤的頂部幾行,如果有的話? –

19

在過去,您可能使用browserHistory推新路徑。這不適用於react-router v4。相反,您已經使用了React的contextroutertransitionTo方法。

這裏有一個簡單的例子:

import React from 'react'; 

class NavigateNext extends React.Component { 
    constructor() { 
    super(); 
    this.navigateProgramatically = this.navigateProgramatically.bind(this); 
    } 

    navigateProgramatically(e) { 
    e.preventDefault(); 

    this.context.router.transitionTo(e.target.href) 
    } 

    render() { 
    return (
     <Link to={"/next-page"} 
      onClick={this.navigateProgramatically} 
     >Continue</Link> 
    ); 
    } 
} 

NavigateNext.contextTypes = { 
    router: React.PropTypes.object 
}; 

transitionTo纔有效router方法之一。 router對象還包含blockTransitions(getPromptMessage),createHref(to)replaceWith(loc)值得檢查。

這裏的official react-router tutorial提到以上方法。 如果你想了解更多關於使用reactcontext請查看docs

+5

我想這樣做,但上下文對象中的路由器變量是未定義的。 「不知何故」它是Link.js中的有效變量。任何想法如何獲得Link.js以外的路由器對象? – singularity

+4

我正在使用redux。如何從動作創建者訪問路由器對象? – Kaidjin

+8

這不適合我。我最終發現,你需要的一切都通過'props'傳遞給你的組件!所有你需要的是'this.props.history.push('/ mypath')'。我在下面的單獨答案中給出了完整的示例:http://stackoverflow.com/a/43250939/1533892 –

13

我沒有足夠的評論聲望,但是在回答@ singularity的問題時,您必須在組件類'contextTypes靜態屬性中包含您希望提供的上下文屬性。

the React docs on context

如果沒有定義contextTypes,那麼上下文將是一個空對象。

在這種情況下:

class NavigateNext extends React.Component { 

    // ... 

    static contextTypes = { 
    router: PropTypes.object 
    } 

    // ... 

} 

不像propTypescontextTypes實際上會導致反應表現不同,不僅是類型檢查。

+0

謝謝你提出這一點。我在propTypes上設置了'router'而不是contextTypes。 – Marek

+0

謝謝這就是缺少的。現在我可以使用this.context.history.goBack() – tjones0808

6

react-router v4 beta發佈了,API改變了一點點。如果您使用的是最新版本,則不要使用this.context.router.transitionTo(e.target.href)做,this.context.router.push(e.target.href)

鏈接到新文檔中:https://reacttraining.com/react-router/#context.router

+5

我正在使用react-router-dom版本4.1.2,我得到這個工作的唯一方法是使用'this.context.router.history.push('/' )'。也許這會幫助別人。 – Marek

0

我發現使用狀態,三元運營商和<Redirect>效果最好。我認爲這也是最好的方式,因爲它最接近v4的設置方式。

在構造函數()

this.state = { 
    redirectTo: null 
} 
this.clickhandler = this.clickhandler.bind(this); 

在渲染()

render(){ 
    return (
     <div> 
     { this.state.redirectTo ? 
      <Redirect to={{ pathname: this.state.redirectTo }} /> : 
      (
      <div> 
       .. 
      <button onClick={ this.clickhandler } /> 
       .. 
      </div> 
      ) 
     } 

在clickHandler()

this.setState({ redirectTo: '/path/some/where' }); 

希望它能幫助。讓我知道。

+0

使用重定向不會導航到新狀態,但會替換歷史記錄中的當前狀態,因此無法導航回去。 –

1

使用withRouter

import React, { PropTypes } from 'react' 
import { withRouter } from 'react-router' 

// A simple component that shows the pathname of the current location 
class ShowTheLocation extends React.Component { 
    static propTypes = { 
    match: PropTypes.object.isRequired, 
    location: PropTypes.object.isRequired, 
    history: PropTypes.object.isRequired 
    } 

    render() { 
    const { match, location, history } = this.props 

    return (
     <div>You are now at {location.pathname}</div> 
    ) 
    } 
} 

// Create a new component that is "connected" (to borrow redux 
// terminology) to the router. 
const ShowTheLocationWithRouter = withRouter(ShowTheLocation) 
+0

這與react-router版本有關嗎? – spik3s

+0

v3和v4,我已經包含鏈接到v4文檔。 – Tomasz

8

使用withRouter將增加路由器的性能給您組件,那麼你就可以訪問history和使用push像你這樣有V3:

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

class Form extends React.Component { 
    constructor(props) { 
    super(props); 

    this.state = { 
     input: '', 
    }; 

    this._submit = this._submit.bind(this); 
    } 

    render() { 
    return (
     <form onSubmit={this._submit}> 
     <input type="text" onChange={(event) => this.setState({input: event.target.value})}/> 

     <button type="submit">Submit</button> 
     </form> 
    ); 
    } 

    _submit(event) { 
    event.preventDefault(); 

    this.props.history.push(`/theUrlYouWantToGoTo`); 
    } 
} 

export default withRouter(Form); 
+3

工作就像一個魅力,任何人都討厭React路由器就像我一樣,這將工作;) –

+0

爲什麼不使用在子組件? – PositiveGuy

5

如果您需要在某個位置的組件之外進行導航您無法從類似於您在舊版本中使用browserHistory的組件中傳入歷史記錄對象,您可以執行以下操作。

首先創建一個歷史模塊

History.js:

import createBrowserHistory from 'history/createBrowserHistory' 

export default createBrowserHistory(); 

然後,當你在聲明路由器確保從反應路由器導入路由器和沒有反應過來路由器-DOM(這是隻是一個包裝反應路由器版本,但會自動創建歷史記錄對象),並通過您剛纔創建

Root.js(或任何你這樣做)的履歷模塊:

import Router from 'react-router/Router' 
import history from './history' 

... 

class Root extends Component{ 
    render() { 
    return (
     <Router history={history}> 
     ... 
     </Router> 
    ); 
    } 
} 

現在您的應用程序將使用您創建的自定義創建的歷史記錄。您現在可以將該歷史模塊導入到任何位置,只需執行history.replace等操作即可,就像您以前使用browserHistory所做的一樣。

SomeModule.js:

import history from './history'; 

export default()=>{ 
    // redirecting to login page using history without having to pass it in 
    // from a component 
    history.replace('/login') 
} 

當然,這並非是推薦的方式就像在舊版本使用browserHistory是不是因爲像服務器端渲染將無法正常工作的推薦方法,但如果你不關心這往往是正確的解決方案。

一個額外的好處做,這是你可以增加歷史記錄對象的事情說謊解析的查詢字符串PARAMS像這樣的例子:

import createBrowserHistory from 'history/createBrowserHistory' 
import queryString from 'query-string'; 

const history = createBrowserHistory(); 

history.location.query = queryString.parse(history.location.search); 

history.listen(() => { 
    history.location.query = queryString.parse(history.location.search); 
}); 

export default history; 
0

如果您需要訪問歷史部件之外(例如,在redux actions)react-router發佈了他們原來的解決方案here

基本上,你必須創建自己的歷史對象:

import { createBrowserHistory } from 'history'; 

const history = createBrowserHistory(); 

,並傳遞給你的路由器:

import { Router } from 'react-router-dom'; 

ReactDOM.render((
    <Router history={history}> // <<-- the history object 
    <App/> 
    </Router> 
), document.getElementById('root')) 

注:必須使用普通路由器,而不是BrowserRouter或HashRouter在這裏!

如果你現在出口history,你可以用它在任何地方工作:

import history from './history'; 

history.push('/home'); 
相關問題