2014-05-13 19 views
41

作爲後續行動Store lifecycle question在Flux架構中,您如何管理客戶端路由/ url狀態?

在一個典型的web應用程序的不錯的一個快捷方式到當前應用狀態通過網址,以便您可以重新訪問該國,並使用前進和後退按鈕來移動各州之間。

在Flux中,我們希望所有操作都通過調度程序,我猜也包含URL更改。你將如何管理flux應用程序中的URL更改?

+0

您能澄清你的意思嗎?在你的問題中的狀態之間進行導航?我認爲你不是指應用程序狀態,而是在路線/ SPA網址之間進行導航。如果是這樣,Flux僅僅是描述內部應用程序通信和控制的應用程序體系結構。 Flux社區一般認爲路由超出了範圍,並且看到Nacho的答案是:反應路由器。 –

回答

43

[更新]

上一堆的反應,和/通量應用的工作後,我來,我更喜歡路由必須分開處理,並垂直於通量的結論。策略是URL /路由應該確定哪些組件被裝入,並且組件根據需要根據路由參數和其他應用程序狀態從商店請求數據。

[原創答案]

的方法,我用了最近的一個項目,同時用助焊劑試驗是使路由層只是一個商店。這意味着更改URL的所有鏈接都會通過調度程序觸發一個操作,請求更新路由。 A RouteStore通過在瀏覽器中設置URL並設置一些內部數據(通過route-recognizer)來響應此調度,以便視圖可以在商店觸發change事件時查詢新的路由數據。

對我來說,一個非顯而易見的部分是如何確保URL更改觸發操作;我最終創建了一個mixin來爲我管理這個(注意:這不是100%強健的,但爲我正在使用的應用程序工作;您可能需要進行修改以適應您的需要)。

// Mix-in to the top-level component to capture `click` 
// events on all links and turn them into action dispatches; 
// also manage HTML5 history via pushState/popState 
var RoutingMixin = { 
    componentDidMount: function() { 
    // Some browsers have some weirdness with firing an extra 'popState' 
    // right when the page loads 
    var firstPopState = true; 

    // Intercept all bubbled click events on the app's element 
    this.getDOMNode().addEventListener('click', this._handleRouteClick); 

    window.onpopstate = function(e) { 
     if (firstPopState) { 
     firstPopState = false; 
     return; 
     } 
     var path = document.location.toString().replace(document.location.origin, ''); 
     this.handleRouteChange(path, true); 
    }.bind(this); 
    }, 

    componentWillUnmount: function() { 
    this.getDOMNode().removeEventListener('click', this._handleRouteClick); 
    window.onpopstate = null; 
    }, 

    _handleRouteClick: function(e) { 
    var target = e.target; 

    // figure out if we clicked on an `a` tag 
    while(target && target.tagName !== 'A') { 
     target = target.parentNode; 
    } 

    if (!target) return; 

    // if the user was holding a modifier key, don't intercept 
    if (!e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey) { 
     e.preventDefault(); 

     var href = target.attributes.href.value; 
     this.handleRouteChange(href, false); 
    } 
    } 
}; 

它將被用作這樣:

var ApplicationView = React.createClass({ 
    mixins: [RoutingMixin], 

    handleRouteChange: function(newUrl, fromHistory) { 
    this.dispatcher.dispatch(RouteActions.changeUrl(newUrl, fromHistory)); 
    }, 

    // ... 
}); 

在店裏處理程序可能看起來像:

RouteStore.prototype.handleChangeUrl = function(href, skipHistory) { 
    var isFullUrl = function(url) { 
    return url.indexOf('http://') === 0 || url.indexOf('https://') === 0; 
    } 

    // links with a protocol simply change the location 
    if (isFullUrl(href)) { 
    document.location = href; 
    } else { 
    // this._router is a route-recognizer instance 
    var results = this._router.recognize(href); 
    if (results && results.length) { 
     var route = results[0].handler(href, results[0].params); 
     this.currentRoute = route; 
     if (!skipHistory) history.pushState(href, '', href); 
    } 

    this.emit("change"); 
    } 
} 
+2

如何將路由集成到調度程序中?一些事件將轉化爲路線。 然後,當有人碰到路線時,調度員可以發送與之相關的事件。 –

+0

@BinaryMuse,你最後得出最新結論的原因是什麼?我正在管理一個相當活躍的更大的Angular應用程序,並且正在轉向FLUX純粹是爲了獲得單一的數據流。知道爲什麼你更喜歡不同的路由解決方案會很有趣,因爲我剛剛開始。 –

+0

@BryanRayner Flux存儲實質上將事件(動作)並降低到存儲在存儲中的狀態更改。路由基本上已經是這樣了:瀏覽器歷史事件就是動作,商店就是當前的URL。我發現在這裏增加額外的複雜性似乎沒有提供任何好處。此外,通過URL中的所有可序列化狀態,清楚了什麼是保存的,哪些不是。 –

2

大多數例子在野外化妝用的React Router,框架基於Ember路由器。重要的部分是組件的聲明式配置的路線配置:

React.render((
    <Router> 
    <Route path="/" component={App}> 
     <Route path="about" component={About}/> 
     <Route path="users" component={Users}> 
     <Route path="/user/:userId" component={User}/> 
     </Route> 
     <Redirect from="/" to="about" /> 
     <NotFoundRoute handler={NoMatch} /> 
    </Route> 
    </Router> 
), document.body)