2017-08-25 28 views
2

據我所知,當一個動作被調用時,所有減速器都會作出響應。如果動作存在於Reducer的switch case語句中,它將執行。如果沒有,則執行保存現有狀態的case: default減速器行爲

當reducer中存在動作但它試圖更新的特定屬性不存在時,它似乎表現良好,因爲沒有任何更新。

例如,我有一個動作創建器,用於設置我的模態的visible屬性。每種模式都有自己的Id。我的代碼如下所示:

export default (state = initialState, action) => { 
    case types.SET_MODAL_IS_VISIBLE: 
     return Object.assign({}, state, 
     { modal22: action.value } 
    )} 

我在多個減速的SET_MODAL_IS_VISIBLE但如果modal22沒有在一個特定的減速定義,什麼都不會發生,沒有錯誤。

現在,我有一個引發錯誤的場景。我有一個通用日期選擇器組件,我可以將它作爲一個單獨的日期選擇器使用,也可以「鏈接到」另一個。如果我需要用戶給我兩個日期,第二種情況非常有用。開始和結束日期。

我還構建了一個功能,如果日期選擇器與另一個日期選擇器耦合,那麼當用戶在第一個日期選擇器中設置日期時,我會禁用第二個日期選擇器中該日期之前的所有日期,不希望用戶無意中選擇開始日期之前的結束日期。

我定義我的日期選取如下:

const initialState = { 
    datePickers: { 
     "startDatePicker": { 
      activeDate: "8/25/2017", 
      disabledBefore: "", 
      linkedTo: "endDatePicker" 
     }, 
     "endDatePicker": { 
      activeDate: "", 
      disabledBefore: "8/25/2017" // This date is set when the user sets the active date in startDatePicker 
      linkedTo: "" 
     } 
    } 
} 

這種情況是有點有趣,因爲在一個屬性的狀態變化,我的減速器觸發另一狀態變化。這並不難,我有辦法控制更新的時間。

設置禁用日期的動作看起來象下面這樣:

... 
case types.SET_DISABLED_DATES: 
    return Object.assign({}, state, 
     datePickers: Object.assign({}, state.datePickers, { 
      datePickers[action.datePickerId]: Object.assign({}, state.datePickers[action.datePickerId], { 
      disabledBefore: action.value 
      }) 
    }) 

請記住,我可以而且應該能夠設置disabledBefore即使日期選取器作爲一個獨立的一個。所以,我需要在每個減速器中使用SET_DISABLED_DATES

我遇到的問題是,每當我呼叫SET_DISABLED_DATES時,我在日期選取器被用作單個/獨立日期的減法器中出現錯誤,因爲其對中的日期選取器ID未在縮減器中定義。

例如,在projectsReducer我可以使用日期選擇器作爲一對的一部分,因此startDatePickerendDatePicker都已定義,並且一切正常。

但我可能會在tasksReducer中使用單個實例日期選擇器,它也響應SET_DISABLED_DATES調用,但由於找不到endDatePicker而失敗。在這種情況下,tasksReducer正在響應我在projectsReducer中設置endDatePickerdisabledDates屬性的呼叫。

我已經張貼了關於這個已經和我在這裏看到的唯一有效的解決這兩個問題是,我需要在我減速的情況,看起來像這樣:

... 
case types.SET_DISABLED_DATES: 
    if(typeof state.datePickers[action.datePickerId] !== "undefined") { // Making sure that what I'm trying to update exists in the reducer 
     return Object.assign({}, state, 
     datePickers: Object.assign({}, state.datePickers, { 
      datePickers[action.datePickerId]: Object.assign({}, state.datePickers[action.datePickerId], { 
      disabledBefore: action.value 
      }) 
    }) 
    } else { 
     return state; 
    } 

誠然,這看起來有點像kludge,但我不能在這裏想出另一個解決方案。

問題是,只要所有減速器對SET_DISABLED_DATES做出響應,就可以保證特定的日期選取器不會在那裏,並且Object.assign()會產生錯誤。

有什麼建議嗎?減速機中的簡單條件是如何進入的呢?這是一個混亂?

P.S.我試過這段代碼,它工作正常,並修復了這個問題。一方面,我認爲這是一種反模式,但另一方面,在嘗試更新它之前,確保我想要在Reducer中更新的屬性是一個好主意。我會很感激你對此的反饋。謝謝。

回答

0

在設置狀態之前,您只是在減速器中進行基本驗證。這很好。我認爲這不是一個好的做法,可以在動作創建器中檢查商店,以防止對不在商店中的物體進行調度(不管怎麼樣,你會這麼做!)。

我不明白的是,日期選擇器如何鏈接到另一個不在商店中的日期選擇器?可能會對組件的didMountwillUnmount發出創建和拆卸操作?

我不知道你的全部要求,但我認爲我們可以使它簡單得多。我會做這樣的事情:

商店:

{ 
    datePickers: { 
    id1: { 
     value: '', 
     minValue: '', 
     maxValue: '', 
    }, 
    id2: { 
     value: '', 
     minValue: '', 
     maxValue: '', 
    } 
    } 
} 

現在,除非你在做某種加上日期選擇器組件,這些組件將總是成對的行爲,我認爲最乾淨的方法是設置您父級組件中的mapDispactchToProps函數中鏈接的日期選擇器中的禁用值。

這就是您要爲組件設置id的地方,並且您確切地知道應該先禁用哪個組件。

喜歡的東西:

dispatch => ({ 
    setArrivalDate(value) { 
    dispatch(datePickerActions.setValue(arrivalDateId, value); 
    dispatch(datePickerActions.setMaxValue(depatureDateId, value); 
    }, 
    setDepatureDate(value) { 
    dispatch(datePickerActions.setValue(depatureDateId, value); 
    dispatch(datePickerActions.setMinValue(arrivalDateId, value); 
    } 
}) 

這可能不是抽象的不夠,但很乾淨。

如果您有配對組件,您可以做同樣的事情,但您仍然需要知道哪個日期在另一個之前。圍繞它進行通用抽象會是一件麻煩事。

+0

沒有100 %明確你的意思是「在** liniked日期選擇器中設置禁用值」,但如果我理解正確,那就是我正在做的事情,每個日期選擇器都必須包含屬於它的數據。屬於'endDatePicker',因此它們存儲在那裏。這裏的關鍵是se在'startDatePicker'中設置日期會導致設置'endDatePicker'中的禁用日期。我在處理函數中很好地控制了這一點。我只需檢查'startDatePicker'的'linkedTo'屬性是否有值。如果是這樣,我打電話給我的動作創建者在'endDatePicker'中設置禁用的日期。 – Sam

+0

每個日期選取器在減速器中都有自己獨特的'Id'。如果你看看我的代碼,'datePickers'對象包含在特定reducer中定義的所有日期選擇器。 'startDatePicker'和'endDatePicker'是'Id's。那裏甚至可能有更多的日期選擇器。你正在努力的部分是這樣的:讓我們說我放在我的文章中的代碼是'reducer1',其中我有'startDatePicker'和'endDatePicker'這兩個配對。在'reducer2'中,我可能有'reservationDatePicker'不配對。 'reducer2'中仍然有'SET_DISABLED_DATES',所以它會響應並給出錯誤。 – Sam

+0

它正在尋找'endDatePicker'來更新,因爲'action.datePickerId'中的內容是這樣,但'endDatePicker'沒有在'reducer2'中定義。 'reducer2'中定義的唯一日期選擇器是'reservationDatePicker'。所以就出現了這個錯誤。 – Sam

0

在你的代碼中刪除加粗部分下方

... 
    case types.SET_DISABLED_DATES: 
     if(typeof state.datePickers[action.datePickerId] !== "undefined") { // Making sure that what I'm trying to update exists in the reducer 
      return Object.assign({}, state, 
      datePickers: Object.assign({}, state.datePickers, { 
       datePickers[action.datePickerId]: Object.assign({}, state.datePickers[action.datePickerId], { 
       disabledBefore: action.value 
       }) 
     }) 
     } else { 
      return state; 
     }

此外,ES6傳播和一個輔助switchcase功能的一點點讓這段代碼更易讀。

const newReducer = (state = defaultState, action) => switchcase({ 
    [types.SET_DISABLED_DATES]: 
    state.datePickers[action.datePickerId] === undefined 
     ? state 
     : ({ ...state, 
     datePickers: { ...state.datePickers, 
      [action.datePickerId]: { ...state.datePickers[action.datePickerId], 
      disabledBefore: action.value, 
      }, 
     }, 
     }), 
})(state)(action.type); 

使用lodash/fp/set,代碼變得

const reducerWithLodash = (state = defaultState, action) => 
switchcase({ 
    [types.SET_DISABLED_DATES]: 
    state.datePickers[action.datePickerId] === undefined 
     ? state 
     : set({...state}, `datePickers.${action.datePickerId}.disabledBefore`, action.value) 
})(state)(action.type) 

我沒有測試lodash版本,所以請採取與一粒鹽(丹·阿布拉莫夫似乎approve

+0

你還在做基本檢查,以確保我試圖更新的屬性在reducer中,這是我的問題。所以我認爲你只是提供一些可讀性改進。 – Sam

+0

啊,我現在明白了。 lodash/fp/set會創建路徑(如果它不存在)。也許你可以修改它的實現來忽略不存在的路徑。 – jaihindhreddy

+0

最近我一直在使用redux,並且我發現如果狀態形狀是靜態的,並且渲染僅基於狀態內容,並且形狀不變,則效果會更好。例如,不是基於屬性存在或不存在,而是始終可以在其中放置一個空對象,並指定是否呈現該對象的另一個布爾屬性。 – jaihindhreddy