2015-11-06 78 views
1

我想通過周圍Redux的一些場景,我沒能找到一個乾淨的解決方案,以這個例子:Redux的派遣行動

比方說,你有一個組件,它是食譜的列表。當您從該列表中選擇一個配方時,您需要dispatch(RECIPE_SELECTED)。異步動作創建者可能會做與配方有關的其他附加信息 - 可能是異步獲取配方的配料,將選擇保存到服務器,無論如何。

在一個完全獨立的組件中,你有一個專業廚師名單。理想的行爲是,當用戶選擇食譜時,與任何具有所選食譜變體的廚師一起填寫專業廚師名單。

你怎麼聽RECIPE_SELECTED,然後調度一個完全不相關的行爲,依賴於配方?喜歡的東西...

when RECIPE_SELECTED:recipe 
    loadChefs(recipe).then(res => dispatch(CHEFS_LOADED, res.chefs)) 

可以混合此loadChefs/dispatchRECIPE_SELECTED行動創造者,但是這是一個關注十分難看的混合和將迅速編織錯綜複雜的。

你也可以做一些非常必要的(即對糧食的終極版)的東西,像這樣(使用反應):

componentWillReceiveProps(nextProps) { 
    if (nextProps.recipe !== this.props.recipe) { 
    const { dispatch, recipe } = nextProps 
    dispatch(loadChefs(recipe)) 
    } 
} 

我真的不喜歡這兩種解決方案。思考?

回答

4

您是否熟悉redux-thunk? https://github.com/gaearon/redux-thunk

隨着應用中間件終極版,形實轉換,你可以做這樣的事情:

function selectRecipe(recipe) { 
    return function (dispatch) { 
     dispatch(setRecipe(recipe)); 
     return loadChefs(recipe).then((res) => 
      dispatch(setChefs(res.chefs)) 
     ); 
    }; 
} 

setRecipesetChefs是簡單的動作創造者。例如

function setRecipe(recipe) { 
    return { 
     type: SET_RECIPE, 
     recipe 
    }; 
} 

function setChefs(chefs) { 
    return { 
     type: SET_CHEFS, 
     chefs 
    }; 
} 

我建議閱讀有關異步操作的文檔。 http://rackt.org/redux/docs/advanced/AsyncActions.html

+0

我認爲你太快剔除了我的問題:「你可以將這個loadChefs/dispatch混合到RECIPE_SELECTED的動作創建者中,但是這實際上是一個嚴重混淆的問題,並且會很快編織一個糾結的網絡。其中一個主要目標是避免模塊之間的混合問題。爲什麼'selectRecipe'創建者必須知道它也應該執行'loadChefs'?它不應該。這是將完全獨立的域混合到同一個動作創建者中......這意味着更遠的路上你將有一個動作創建者在一個模塊中,該模塊持有關於數十個其他模塊的領域知識。 – Clev3r

+0

我不同意。如果選擇一個新配方導致加載廚師名單,那麼他們不是單獨的關注。在redux中沒有這樣的動作監聽器。任何複雜的邏輯(特別是異步的東西)都屬於您的行爲創建者。你可以做的是將他們分成簡單和複雜的動作創作者,如上所示。 –

0

不同的解決方案是使用Redux-Saga中間件。這讓你寫這樣的事情:

function* loadChefsSaga() { 
    # takeLatest sets up a system that spawns the inner generator every time 
    # an action matching the pattern is dispatched. If the inner generator 
    # is still running when additional actions are dispatched, it is cancelled, 
    # and a new one is spawned. 
    yield takeLatest('RECIPE_SELECTED', function* (recipe) { 
     # When a Promise is yielded, the generator is resumed when the Promise 
     # resolves. Alternatively, if it rejects, the rejected value is thrown 
     # into this generator. 
     const {chefs} = yield loadChefs(recipe) 

     # Assuming chefsLoaded is an action creator for CHEFS_LOADED 
     # `put` is redux-saga's equivelent of `dispatch`. We use it because 
     # the saga doesn't have direct access to the `dispatch` function. 
     yield put(chefsLoaded(chefs)) 
    }) 
} 

我假設你已經基本熟悉javascript生成器的工作。如果沒有,去看看他們;他們是一個強大的模式。在這種情況下,redux-saga使用它們來構建可以阻止事物的函數。每當產生某種東西時,redux-saga將它視爲它知道如何處理的「效果」。例如,當產生一個Promise時,redux-saga會設置它,以便當Promise解析時(或者如果它拒絕時將其放入生成器)恢復生成器。