我们目前正在使用<app-route>
进行路由,现在正在使用polymer-redux
实施Redux。但是,目前还不清楚将两者结合起来的最佳方法是什么。由于<app-route>
维持自己的状态,我们无法将其存储在Redux商店中。但是,对于用户可以执行的某些操作,我们还希望更新URL。
我目前的想法是使用中间件做某些事情,但我不清楚如何最好地从该中间件中访问/修改<app-route>
中的路由。我们怎样才能最好地接近这个?
答案 0 :(得分:4)
我自己遇到了同样的问题。冲突是redux的主要原则是它倡导一种独特的集中式状态,而聚合物主张多个分散式状态。调和两者显然需要不时进行一些黑客攻击。通过聚合物(与app-route一起使用)将redux状态的浏览器URL同步是一个很好的例子。使用app-location,我所做的就是:
<!-- my-app.html -->
<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="/bower_components/app-route/app-location.html">
<link rel="import" href="/src/redux/redux-behavior.html">
<link rel="import" href="/src/redux/route-action-creator.html">
<dom-module id="my-app">
<template>
<app-location route="{{_browserRoute}}"></app-location>
</template>
<script>
Polymer({
is: 'my-app',
behaviors: [ReduxBehavior, RouteActionCreator],
properties: {
_browserRoute: {
type: Object
}
},
observers: [
'_browserRouteChanged(_browserRoute.*)'
],
_browserRouteChanged: function(browserRoute) {
this.dispatch('browsed', browserRoute.base)
}
})
</script>
</dom-module>
我们使用双向绑定来保持my-app的_browserRoute与浏览器网址同步。更改后,它会调度“已浏览”操作,该操作会将状态中的当前路由减少为_browserRoute的新值。
<!-- route-action-creator.html -->
<script>
const ROUTE_ACTIONS = {}
const routeActions = {}
ROUTE_ACTIONS.BROWSED = 'ROUTE_BROWSED'
routeActions.browsed = function(route) {
return {
type: ROUTE_ACTIONS.BROWSED,
route: route
}
}
ROUTE_ACTIONS.REDIRECTED = 'ROUTE_REDIRECTED'
routeActions.redirected = function(path) {
window.history.pushState({}, null, path)
window.dispatchEvent(new CustomEvent('location-changed'))
return {
type: ROUTE_ACTIONS.REDIRECTED,
path: path
}
}
/**
* Route action creator behavior
* @polymerBehavior
*/
RouteActionCreator = {
actions: routeActions
}
</script>
<!-- route-reducer.html -->
<link rel="import" href="/src/redux/route-action-creator.html">
<script>
const reducers = {}
reducers.routeReducer = function(state, action) {
if (!state) {
return {
current: null
}
}
switch (action.type) {
case ROUTE_ACTIONS.BROWSED:
return Object.assign({}, state, {
current: action.route
})
case ROUTE_ACTIONS.REDIRECTED:
// Browser state is reduced in action creator because it uses an event
return state
default:
return state
}
}
</script>
现在我可以绑定
了statePath:'route.current'
到我的应用中的任何app-route组件。我也可以用
this.dispatch('redirected','/ some-path')
用于重定向和标准锚标记,因为浏览器将与状态同步。
答案 1 :(得分:2)
我会创建一个包含app-route
的非可视聚合物元素,并实现polymer-redux
行为并处理app-route
和redux
商店之间的互动。
以下是向导的这种元素的示例(包含要加载的步骤和分析):
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/app-route/app-location.html">
<link rel="import" href="../bower_components/app-route/app-route.html">
<dom-module id="my-router">
<style>
</style>
<template>
<app-location route="{{route}}" id="location" use-hash-as-path></app-location>
<app-route
active="{{active}}"
route="{{route}}"
pattern="/:step/:id"
data="{{page}}"
tail="{{tail}}">
</app-route>
</template>
<script>
(function() {
'use strict';
Polymer({
is: 'my-router',
behaviors: [ ReduxBehavior ],
properties: {
page: {
type:Object,
value: function(){ return {};},
},
currentAnalysisId: {
type:String,
statePath: 'id',
},
currentStep: {
type:String,
statePath:'currentStep',
},
active:{
type: Boolean,
observer:'_onInValidRoute'
}
},
observers: [
'_changeRoute(page)',
'_changeStepOrAnalysisId(currentStep,currentAnalysisId)',
],
actions: {
changeStep: function(step) {
return {
type: 'CHANGE_CURRENT_STEP',
step,
}
},
loadAnalysis: function(id,step) {
return {
type: 'LOAD_ANALYSIS',
id,
step,
}
},
},
_initialized : false,
ready: function() {
// required otherwise if navigating to a sub-route for the first time will be reset, by the localstorage initial redux state update
this._initialized = true;
},
_changeRoute: function(page) {
// required because otherwise
this.debounce('route_update',()=>{
let step = page.step;
let id = page.id;
if (id && this.getState().id !== id) {
ga('send', 'event', 'Analysis', 'load');
this.dispatch('loadAnalysis',id,step)
}
else if (this.getState().currentStep !== step) {
this.dispatch('changeStep',step);
}
ga('send', 'pageview');
},1);
},
_changeStepOrAnalysisId: function(step, id) {
if (!this._initialized) {
return;
}
this.debounce('state_changed',()=>{
if (this.page.step !== step || (this.page.id !== id && this.page.id !== '' && id !== null)) {
this.page = {step,id};
}
})
},
_onInValidRoute: function(valid) {
if (!valid) {
this.async(()=> {
this.$.location.set('route.path','/start/');
})
}
},
});
})();
</script>
</dom-module>