2015-11-27 17 views
12

我一個反應,和/終極版應用程序,它允許「小工具」的工作被添加到一個頁面,在二維空間操縱。需要一次選擇和操作多個小部件。我目前的狀態樹的簡化版本,看起來像下面...如何撰寫Redux的減速與相關國家

{ 
    widgets: { 
    widget_1: { x: 100, y: 200 }, 
    widget_2: { x: 300, y: 400 }, 
    widget_3: { x: 500, y: 600 } 
    }, 
    selection: { 
    widgets: [ "widget_1", "widget_3" ] 
    } 
} 

我目前有此樹由2級減速器一個管理widgets狀態和其他管理selection狀態管理。選擇狀態減速機可以簡化爲(注:我使用Immutable.js太)...

currentlySelectedWidgets(state = EMPTY_SELECTION, action) { 
    switch (action.type) { 
    case SET_SELECTION: 
     return state.set('widgets' , List(action.ids)); 
    default: 
     return state 
    } 
} 

這一切似乎工作得非常好,但是我現在有我努力融入這個要求模型...

對於我需要有對當前選擇UI目的具有的x/y屬性是反射當前選擇的窗口小部件的x/y座標的左上角。示例狀態可能看起來像......

{ 
    widgets: { 
    widget_1: { x: 100, y: 200 }, 
    widget_2: { x: 300, y: 400 }, 
    widget_3: { x: 500, y: 600 } 
    }, 
    selection: { 
    x: 100, 
    y: 200, 
    widgets: [ "widget_1", "widget_3" ] 
    } 
} 

這裏的邏輯非常簡單,找到min()所有選定的部件xy值,但我不確定我應該如何處理這種減速器組成,因爲currentlySelectedWidgets減速器無法訪問狀態樹的widgets部分。我考慮過...

  • 將reducer合併爲一個reducer:這似乎不是一個很好的可擴展解決方案。
  • 傳遞部件的現有的清單,行動圍繞:這似乎特別討厭。
  • 找到一個更好的方式來狀態樹模型:我有一個看法,但任何替代版本似乎有其他缺點。
  • 構建定製combineReducers()可以養活小部件列表進入我的currentlySelectedWidgets()減速機作爲一個額外的參數:我想這可能是我最好的選擇。

我很想聽聽其他人如何管理類似情況的其他建議,似乎管理「當前選擇」必須是其他人必須解決的常見狀態問題。

回答

8

這是一個很好的用例Reselect

創建一個選擇器來訪問你的狀態,並返回衍生數據。

作爲一個例子:

import { createSelector } from 'reselect' 

const widgetsSelector = state => state.widgets; 
const selectedWidgetsSelector = state => state.selection.widgets; 

function minCoordinateSelector(widgets, selected) { 
    const x_list = selected.map((widget) => { 
    return widgets[widget].x; 
    }); 

    const y_list = selected.map((widget) => { 
    return widgets[widget].y; 
    }); 

    return { 
    x: Math.min(...x_list), 
    y: Math.min(...y_list) 
    }; 
} 

const coordinateSelector = createSelector(
    widgetsSelector, 
    selectedWidgetsSelector, 
    (widgets, selected) => { 
    return minCoordinateSelector(widgets, selected); 
    } 
); 

coordinateSelector現在提供訪問自己分鐘xy性質。只有當widgetsSelectorselectedWidgetsSelector狀態發生更改時,上述選擇器纔會更新,使其成爲訪問您之後的屬性的非常高效的方式,並避免狀態樹中的重複。

+3

在嘗試實施Hummlas的基於thunk的方法和基於重新選擇的方法之後,我覺得這種方法更好地適應了我的模型,並提供了更清潔和更易維護的解決方案。我想我慢慢學習的一般經驗法則是不在狀態樹中存儲任何可以從現有樹中重新計算的東西。 – andykent

+1

忘記了,在這個例子的答案中有一個小錯誤,最後一個'minSelector(widget,selected)'應該讀爲'minCoordinateSelector(widgets,selected)',不太可能任何人都會按原樣複製這段代碼,但只是爲了以防萬一他們是這樣。 – andykent

4

如果使用thunk中間件(https://github.com/gaearon/redux-thunk),則可以使SET_SELECTION操作變爲thunk,這將允許它在發出將由Reducer接收的調度之前讀取整個狀態。

// action creator 
function setSelection(selectedWidgetId) { 
    return (dispatch, getState) => { 
     const {widgets} = this.getState(); 
     const coordinates = getSelectionCoordinates(widgets, selectedWidgetIds); 

     dispatch({ 
      type: SET_SELECTION, 
      payload: { 
       widgets: selectedWidgets, 
       x: coordinates.x, 
       y: coordinates.y 
      } 
     }); 
} 

這樣,你讓你在選擇減速所需要的所有信息,而不必沿所有控件對象到你的動作列表中通過。

+0

感謝這個,好像它會解決這個問題,我我很感興趣,看看隨着代碼庫的增長,這種方法的效果如何,但我們會看到。 – andykent

+0

今天我不能再打開任何問題,所以我問這個問題。 如果@Ashley想從'currentlySelectedWidgets(state,action)'reducer中刪除一個特定的部件會怎麼樣?這個怎麼做? – zatziky

0

我使用這個:

CommonReducer.js

export default function commonReducer(state = initialState.doesNotMatter, payload) { 
    switch (payload.type) { 
    case "JOINED_TYPE": { 
     let newState = Object.assign({}, state); 
     return newState; 
    } 
    default: 
     return state; 
    } 
} 

SomeOtherReducer.js

import commonReducer from './commonReducer'; 

export default function someReducer(state = initialState.somePart, payload) { 
     switch (payload.type) { 
     case "CUSTOM_TYPE": { 
      let newState = Object.assign({}, state); 
      return newState; 
     } 
     default:{ 
      return commonReducer(state, payload); 
     } 
     } 
}