2016-03-02 55 views
3

我有一個reactJs應用程序,現在我正在學習Redux以將其用作Flux實現。Redux:只有同步調用允許從reducer功能?

我創建了一個商店,我已經創建了我的第一個減速功能,但現在我有一些問題,來我的心,請幫我明白了。

正如你可以看到我有一個名爲「FIND_PRODUCTS」這基本上是從後端服務中獲取數據的作用。爲了調用這個後端服務,我基本上使用了一個異步的ajax調用,所以基本上我面臨的問題是,在我的後端調用完成之前,狀態是從reducer函數返回的,因此狀態沒有正確更新,並且用戶該商店正在獲取不正確的數據。如果我切換到同步調用,這個問題就解決了,但是,我得到的第一個警告是應該避免同步調用,因爲它可能會降低用戶的體驗(性能)。

所以我的問題,可我們只從同步減速功能獲取數據? 提取數據是否應該在reducer函數中發生,或者有另一種方法可以做到這一點?如果是這樣,那是什麼?

是否具有單一的對象樹與大型應用程序維護國家尺度很好的終極版這種模式?如果我有1000個動作,那麼我的減速器功能中的開關就會很大!我們如何避免這種情況?

謝謝!

const initialState = { 
availableLocales: [{text: 'En'}, {text: 'Es'}, {text: 'Fr'}], 
selectedLocale: 'En', 
translations: i18n.getTranslations(), 
products: [] 
}; 


const reducer = (state = initialState, action = {type: 'NONE'})=> { 

//To make the reducer a pure function 
deepFreeze(state); 
deepFreeze(action); 
switch (action.type) { 
    case 'SWITCH_LOCALE': 
     let newState = Object.assign({}, state, { 
      selectedLocale: action.locale, 
      translations: i18n.getTranslations(action.locale) 
     }); 
     return newState; 
    case 'FIND_PRODUCTS': 
     let newState = Object.assign({}, state, { 
      products:ProductHelper().findProductsByProductType(action.productType) 
     }); 
     return newState; 

    default: 
     return state 
} 
return state; 
} 

// Create a Redux store holding the state of your app. 
// Its API is { subscribe, dispatch, getState }. 
const store = createStore(reducer); 

// You can subscribe to the updates manually, or use bindings to your view layer. 
store.subscribe(() => 
    console.log(store.getState()) 
); 

export default store; 
+0

組件不能調用componentWillMount方法的findProduct異步函數嗎? – Raulucco

+0

當然,但是我們需要什麼店?我認爲這一切的關鍵是將國家從零部件轉移到商店.. – fgonzalez

回答

5

考慮一下:

創建actions.js文件和導出操作這樣的功能:

import * as types from '../constants/action_types'; 
import * as api from '../utils/api' 

export function something1(someId){ 

    return (dispatch) => { 

     dispatch({type: `${types.SOMETHING1}_PENDING`}); 

     api.getSomething(someId) 

      .then((res) => { 
       dispatch({ 
        type: `${types.SOMETHING1}_SUCCEEDED`, 
        somethings: res.body 
       }); 

      .catch((err) => { 

       dispatch({ 
        type: `${types.SOMETHING1}_FAILED`, 
        errors: err.body 
       }) 

      }); 
    } 
} 

export function something2(someOtherId){ 

    return (dispatch) => { 

     dispatch({type: `${types.SOMETHING2}_PENDING`}); 

     api.getSomething2(someOtherId) 

      .then((res) => { 
       dispatch({ 
        type: `${types.SOMETHING2}_SUCCEEDED`, 
        otherThings: res.body 
       }); 

      .catch((err) => { 

       dispatch({ 
        type: `${types.SOMETHING2}_FAILED`, 
        errors: err.body 
       }) 

      }); 
    } 
} 

然後,狀態時,你有數據

下一個單獨的文件分開你的減速,並創建一個文件導出所有這些只改變 這樣的減速/ index.js:

export { default as reducer1 } from './reducer1'; 
export { default as reducer2 } from './reducer2'; 
export { default as reducer3 } from './reducer3'; 
export { default as reducer4 } from './reducer4'; 

然後配置你的店是這樣的:

configure_store.js

import { createStore, combineReducers, applyMiddleware } from 'redux'; 
import thunk from 'redux-thunk'; 
import * as reducers from '../reducers'; 

const rootReducer = combineReducers(reducers); 
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore); 

export default function configureStore(initialState) { 
    return createStoreWithMiddleware(rootReducer, initialState); 
} 

最後添加到您的根:

import configureStore from '../store/configure_store'; 

const store = configureStore(); 

class Root extends Component { 

    render() { 
     return (
      ... 
      <Provider store={ store } > 
      ... 
      </Provider> 
     ); 
    } 
} 

export default Root; 
3

首先,你不能在reducer中獲取數據,因爲它需要通過redux定義來純化。您應該創建動作創建器,它將異步獲取數據並將其傳遞給Reducer。行動可能不純。

在這裏,你可以閱讀更多http://redux.js.org/docs/advanced/AsyncActions.html

您也可以使用中間件像redux-thunk對此進行簡化。 https://github.com/gaearon/redux-thunk


至於第二個問題,你可以在你的應用程序超過一個減速。而不是將它們與combineReducers(...)函數結合使用http://redux.js.org/docs/basics/Reducers.html

1

不應該在Reducer中使用ProductHelper()來請求數據。

相反,你應該用行動創造者派遣,從您的API請求數據的操作。您的API中間件會返回一個承諾,在完成時將分派一個動作意圖與有效負載,以使reducer消耗並返回下一個狀態。

我建議你看看Redux ThunkRedux API middleware

2

正如redux documentation說,減速應該是純功能,所以它不應該做的Ajax請求。

更好的方法是使用redux-thunk中間件,它允許您在一個操作中多次調用dispatch

所以,在你的榜樣,你做這樣的事情:

// definition of action creator 
function loadProducts(productType) { 
    return {type: 'FIND_PRODUCTS', productType: productType} 
} 

... 
// calling dispatch of your action 
dispatch(loadProducts(productType)); 

但隨着終極版 - 咚你的行動的創建者會是這樣的:

function loadProducts(productType) { 
    return function(dispatch){ 
    dispatch({type: 'FIND_PRODUCT_STARTED'}); 
    // I don'h know how findProductsByProductType works, but I assume it returns Promise 
    ProductHelper().findProductsByProductType(productType).then(function(products){ 
     dispatch({type: 'FIND_PRODUCT_DONE', products: products}); 
    }); 
    } 
} 

而且你的減速將成爲純函數:

... 
case 'FIND_PRODUCTS_DONE': 
    let newState = Object.assign({}, state, { 
     products: action.products, 
    }); 
    return newState; 
... 

在這種情況下,您還可以處理加載狀態,即設置loading當你的狀態爲真時action.typeFIND_PRODUCT_STARTED

在我的例子中,我假設findProductsByProductType返回Promise。在這種情況下,你甚至可以使用redux-promise-middleware沒有終極版,形實轉換,它會做所有的工作對您:

function loadProducts(productType) { 
    return { 
    type: 'FIND_PRODUCT', 
    payload: { 
     promise: ProductHelper().findProductsByProductType(productType) 
    } 
    } 
}