2015-12-16 32 views
4

我正在尝试将Redux集成到我的React项目中。 目前我没有使用任何Flux框架。Redux:使用异步中间件vs调度成功函数

我的应用从API获取一些数据,并将其显示在一个漂亮的方式,像这样:

componentDidMount() { 
    getData(); 
} 

getData() { 
    const self = this; 

    ajax({ 
    url: apiUrl, 
    }) 
    .success(function(data) { 
    self.setState({ 
     data: data, 
    }); 
    }) 
    .error(function() { 
    throw new Error('Server response failed.'); 
    }); 
} 

在阅读终极版,我已经看中了,我可以用处理存储两种可能的方法在店里我成功的数据:

  • 使用异步中间件,或
  • 从AJAX功能的成功回调调度动作ADD_DATA

但我不确定哪种方法更好。

回调中的调度操作听起来很容易实现和理解,而异步中间件很难向不习惯使用功能语言的人解释。

回答

10

我个人比较喜欢使用自定义中间件做到这一点。它使行动更容易遵循,并且具有更少的样板IMO。

我已经设置了我的中间件来查找匹配特定签名的操作返回的对象。如果找到这个对象模式,它会专门处理它。

例如,我使用看起来像这样的动作:

export function fetchData() { 
    return { 
    types: [ FETCH_DATA, FETCH_DATA_SUCCESS, FETCH_DATA_FAILURE ], 
    promise: api => api('foo/bar') 
    } 
} 

我定制的中间件看到该对象有一个types阵列和promise功能以及专门处理它。这里是什么样子:

import 'whatwg-fetch'; 

function isRequest({ promise }) { 
    return promise && typeof promise === 'function'; 
} 

function checkStatus(response) { 
    if (response.status >= 200 && response.status < 300) { 
    return response; 
    } else { 
    const error = new Error(response.statusText || response.status); 
    error.response = response.json(); 
    throw error; 
    } 
} 

function parseJSON(response) { 
    return response.json(); 
} 

function makeRequest(urlBase, { promise, types, ...rest }, next) { 
    const [ REQUEST, SUCCESS, FAILURE ] = types; 

    // Dispatch your request action so UI can showing loading indicator 
    next({ ...rest, type: REQUEST }); 

    const api = (url, params = {}) => { 
    // fetch by default doesn't include the same-origin header. Add this by default. 
    params.credentials = 'same-origin'; 
    params.method = params.method || 'get'; 
    params.headers = params.headers || {}; 
    params.headers['Content-Type'] = 'application/json'; 
    params.headers['Access-Control-Allow-Origin'] = '*'; 

    return fetch(urlBase + url, params) 
     .then(checkStatus) 
     .then(parseJSON) 
     .then(data => { 
     // Dispatch your success action 
     next({ ...rest, payload: data, type: SUCCESS }); 
     }) 
     .catch(error => { 
     // Dispatch your failure action 
     next({ ...rest, error, type: FAILURE }); 
     }); 
    }; 

    // Because I'm using promise as a function, I create my own simple wrapper 
    // around whatwg-fetch. Note in the action example above, I supply the url 
    // and optionally the params and feed them directly into fetch. 

    // The other benefit for this approach is that in my action above, I can do 
    // var result = action.promise(api => api('foo/bar')) 
    // result.then(() => { /* something happened */ }) 
    // This allows me to be notified in my action when a result comes back. 
    return promise(api); 
} 

// When setting up my apiMiddleware, I pass a base url for the service I am 
// using. Then my actions can just pass the route and I append it to the path 
export default function apiMiddleware(urlBase) { 
    return function() { 
    return next => action => isRequest(action) ? makeRequest(urlBase, action, next) : next(action); 
    }; 
} 

我个人很喜欢这种方法,因为它集中了大量的逻辑,让您的API操作是如何构成的标准执行。缺点是对那些不熟悉redux的人来说可能有点神奇。我也使用thunk中间件,这两者一起解决我迄今为止的所有需求。

+2

我在我的应用程序中使用了类似的方法。如果您希望中央地点派发响应API相关事件的操作,这也是一种很好的方法。例如。显示/隐藏加载程序,在401响应的情况下将用户路由到登录屏幕。如果没有这些,那么在您调用API调用的任何地方都会散布这样的操作。来自Angular,这有助于我实现拦截器完成的功能。 – akaashanky

+0

如果我想用中间件使用redux-thunk ..我可以在@ nross83上执行它吗?我需要派遣一个API调用和成功几次派发 –

+1

@HarkiratSaluja是的,只需添加thunk中间件(以及您的定制中间件),它就可以工作。您可以使用Redux的applyMiddleware功能并传入您想要使用的所有中间件。我在我自己的项目中使用了thunk以及自定义中间件。 http://redux.js.org/docs/api/applyMiddleware.html – nross83

3

我使用redux-thunk使ajax调用和redux-promise来处理承诺,如下所示。

function getData() {    // This is the thunk creator 
    return function (dispatch) { // thunk function 
     dispatch(requestData());  // first set the state to 'requesting' 
     return dispatch(
     receiveData(    // action creator that receives promise 
      webapi.getData()   // makes ajax call and return promise 
     ) 
    ); 
    }; 
    } 

调度回调的动作看起来简单,对于第一次出国去理解,但使用中间件具有以下优点:

  • 的thunk允许派遣多个动作(如上面的例子 - 第一次设置状态为'请求',可以通过加载指示器使用, 等)
  • 它允许有条件地分派额外的动作。例如,只获取如果自最后一次取超过阈值
  • 您仍可以实现这一切,没有中间件,但使用中间件可以帮助你保持动作的创造者中的所有异步行为
+0

是的,我知道。但是有没有关于这个方法的好东西,那么简单的ajax回调? –

2

两种方法都不好,因为它们是相同的。无论你在派遣回调或使用终极版的thunk操作时,实际上是在做以下几点:

function asyncActionCreator() { 
    // do some async thing 
    // when async thing is done, dispatch an action. 
} 

我个人更喜欢跳过中间件/的thunk,只是使用回调。我真的不认为中间件/的thunk相关的额外开销是必要的,这不是真的那么难写自己的“异步操作的创造者”的功能:

var store = require('./path-to-redux-store'); 
var actions = require('./path-to-redux-action-creators'); 

function asyncAction(options) { 
    $.ajax({ 
    url: options.url, 
    method: options.method, 
    success: function(response) { 
     store.dispatch(options.action(response)); 
    } 
    }); 
}; 

// Create an async action 
asyncAction({ 
    url: '/some-route', 
    method: 'GET', 
    action: actions.updateData 
}); 
1

我想你实际上问的是是否在动作创建者或组件中使用AJAX调用。

如果您的应用程序足够小,则可以在组件中使用它。但随着您的应用程序变大,您将需要重构。在更大的应用程序中,您希望组件尽可能简单和可预测。在你的组件中进行AJAX调用会大大增加它的复杂性。此外,在动作创建者中进行AJAX调用使其更具可重用性。

惯用的Redux方式是将所有的异步调用放入动作创建者。这使得您的应用的其余部分更具可预测性。你的组件总是同步的。你的减速器总是同步的。

异步操作创建者的唯一要求是redux-thunk。您不需要知道中间件的来龙去脉如何使用redux-thunk,您只需知道如何在创建商店时应用它。

以下是直接取自redux-thunk GitHub的页面:

import { createStore, applyMiddleware } from 'redux'; 
import thunk from 'redux-thunk'; 
import rootReducer from './reducers/index'; 

// create a store that has redux-thunk middleware enabled 
const createStoreWithMiddleware = applyMiddleware(
    thunk 
)(createStore); 

const store = createStoreWithMiddleware(rootReducer); 

就是这样。现在您可以拥有异步操作创建器。

此致应该是这样的:

function getData() { 

    const apiUrl = '/fetch-data'; 

    return (dispatch, getState) => { 

     dispatch({ 
      type: 'DATA_FETCH_LOADING' 
     }); 

     ajax({ 
      url: apiUrl, 
     }).done((data) => { 
      dispatch({ 
       type: 'DATA_FETCH_SUCCESS', 
       data: data 
      }); 
     }).fail(() => { 
      dispatch({ 
       type: 'DATA_FETCH_FAIL' 
      }); 
     }); 

    }; 

} 

就是这样。每当动作创建者返回一个函数时,thunk中间件就暴露dispatch(和getState,您可能不需要)来允许异步操作。