2016-06-10 120 views
0

減速機對動作的新手問題。 From redux documentation減速機vs動作

操作描述了事情發生的事實,但不響應指定 如何應用程序的狀態變化。

由於相同的參數,減速機應計算下一個狀態和 返回。沒有驚喜。無副作用。沒有API調用。沒有突變。 只是一個計算。

因此,如果我們考慮以下情形:

  1. 用戶可以將地圖上的點,並得到這些點之間的路線。
  2. 當用戶第一次點擊地圖時,這是他的出發點。當他第二次點擊 - 這是他的終點。隨後的點擊會在前一個點和結束位置之間添加點。
  3. 每個點後,加入(除了第一個)必須新點和前一個點之間計算出的路線。所以如果我有S - > A - > F並加點B(S - > A - > B - > F)必須計算兩條路線A→B和B→F

因此,的有兩個副作用在添加任何3+點:

  • 新點位於不在名單
  • 新航線必須計算到終點的結束。

如果我將點結構的模型,因爲這:

// typescript 
interface Point { 
    coordinates; 
    routeTo?; 
} 

我是正確執行的項目位置計算和操作路線檢索,如:

// pseudo code 
    export function addMapPoint(newPoint) { 
     return (dispatch, getState) => { 
     const {points} = getState(); 
     const position = getInsertPosition(points, newPoint) 
     dispatch(addPoint(newPoint, position)); 
     if (points.length >= 2) { 
      const previousPoint = getPreviousPoint(points, position); 
      calculateRoute(newPoint, previousPoint).then(route => { 
      dispatch(updateRoute(newPoint, route)) 
      }) 
     } 
     } 
    } 

對我來說,這在某種程度上違背到「但不指定應用程序的狀態如何改變」 - 因爲從行動我指定插入我的新點的位置。
我可以計算減速器中的插入位置,但是如何獲取完成點的路徑數據?
這裏的正確方法是什麼?

回答

1

假設我們有一個calculateRoute函數接受兩個點,並返回解決它們之間的路由的承諾。

首先,讓我們創建一個簡單的動作的創造者,所以我們知道我們的點被正確地存儲:

let addPoint = (point, index) => { 
    return { 
     type: 'ADD_POINT', 
     point: point, 
     index: index 
    } 
} 

那麼,讓我們來處理在減速這個動作:

let reducer = (state = { points: [] }, action) => { 
    switch (action.type) { 
     case 'ADD_POINT': 
      return Object.assign({}, state, { 
       points: [ 
        ...state.points.slide(0, action.index), 
        action.point, 
        ...state.points.slide(action.index + 1) 
       ] 
      }); 
     default: 
      return state; 
    } 
} 

現在,用戶之後添加一個點,我們使用addPoint創建一個動作併發送它,到目前爲止這麼好,但這是一件容易的事情。

我爭取的結構是在我的減速器的路線列表太多,所以讓我們來擴展它來支持:

let reducer = (state = { points: [], routes: [] }, action) => { 
    switch (action.type) { 
     case 'ADD_POINT': 
      return Object.assign({}, state, { 
       points: [ 
        ...state.points.slide(0, action.index), 
        action.point, 
        ...state.points.slide(action.index + 1) 
       ] 
      }); 
     case 'UPDATE_ROUTES': 
      return Object.assign({}, state, { 
       routes: action.routes 
      }); 
     default: 
      return state; 
    } 
} 

而且行動的創建者將是:

let updateRoutes = (routes) => { 
    return { 
     type: 'UPDATE_ROUTES', 
     routes: routes 
    } 
} 

請注意我們覆蓋了整個路線集合。現在可以,但是可能在生產系統中,您希望稍微優化一下。

現在我們實際上需要編寫一些邏輯。我將假設一個方便的假設,即我們有一個calculateRoutes可獲取點的集合,並返回解析各個路由列表的promise,每個路由將是一個包含兩個點和實際路由的對象。話雖如此,我們現在thunk看起來就像這樣:

addPointAndUpdateRoutes = (point, index) => { 
    return (dispatch, getState) => { 
     // First, update the point list 
     dispatch(addPoint(point, index)); 

     // Now, recalculate routes 
     calculateRoutes(getState().points) 
      .then(routes => dispatch(updateRoutes(routes)); 
    }; 
}; 

這是我的意見的方式更好。


現在,關當然是假設我們有一個神奇的calculateRoutes功能是不是一個嚴重的假設,但它不是以實現優化的方式這個功能超硬任務(實際上意味着向服務器發送唯一途徑,我們之前沒有計算等)。 但是這只是邏輯和而不是應用程序的狀態,因此,只要您保留由商店和縮減器定義的「合同」,您就可以隨意以任何方式實現它。

希望這可以幫助你。

+0

所以看看你提出的'addPointAndUpdateRoutes =(point,index)'和'addPoint(point,index)'簽名 - 索引計算是否被移動到了ui?但是如果我可以通過點擊手動輸入座標的地圖來添加一個點呢?關於'calculateRoutes'如果我有100分呢?或200?每次遍歷整個列表,檢查一些緩存(又一個狀態......),並且用戶是否可以始終從頭開始?那麼我必須使該緩存失效。 – gerasalus

+0

我猜你可以在UI中計算'index',我的意思是在兩個已有點之間添加一個點可以在UI中非常清晰地表達出來。關於'calculateRoutes' - 你需要一個memoized函數,這意味着如果它已經計算出一個路由,它會立即返回它,這個實現需要在你的客戶端和你的服務器上。如果用戶有100或1000或甚至5000點,那麼它並不重要,只要沒有真正計算,它會發生得非常快。無論如何,如果你支持100分,你將不得不計算它們,不管UI的實現。 –

+0

以及我的觀點是,我可能有兩個,四個或五個不同的地方,我可以添加一個點。邏輯保持不變 - 如果第一個點 - 添加,第二個 - 添加它,如果第三個 - 在最後一個之前添加等等。所以這個邏輯不應該在每個UI組件中被複制。如果我使用聰明的calculateRoutes方法,這可以在reducer中完成(但是是嗎?)。或者爲什麼我應該分開存放路線 - 他們不能沒有一點生活。如果無法達到某個點,則可能沒有路線,從而導致「終點」也可能無法到達的副作用。 – gerasalus