Redux:在成功函数

时间:2015-12-16 05:09:45

标签: asynchronous reactjs redux redux-thunk

我正在尝试将Redux集成到我的React项目中。 目前我没有使用任何Flux框架。

我的应用从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.');
  });
}

在阅读有关Redux的文章时,我已经确定了两种可行的方法,可用于处理在商店中存储我的成功数据:

  • 使用异步中间件或
  • 从ajax函数的成功回调
  • 调度操作ADD_DATA

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

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

4 个答案:

答案 0 :(得分: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中间件,这两者一起解决了我到目前为止的所有需求。

答案 1 :(得分: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
        )
      );
    };
  }

对于初学者来说,在回调中调度操作似乎更简单,但使用中间件具有以下优点:

  • thunks允许分派多个动作(如上例所示 - 首先将状态设置为“请求”,可以通过加载指示符来使用, 等)
  • 它允许有条件地发送其他操作。例如,仅在自上次提取以来的时间超过阈值时获取
  • 您仍然可以在没有中间件的情况下实现所有这些,但使用中间件可以帮助您在动作创建者中保留所有异步行为

答案 2 :(得分:2)

这两种方法都不是更好,因为它们是相同的。无论是在回调中调度操作还是使用redux thunk,您实际上都在执行以下操作:

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

我个人更喜欢跳过中间件/ thunk并只使用回调。我并不认为与中间件/ thunk相关的额外开销是必要的,并且编写自己的异步动作创建者&#34;并不是那么困难。功能:

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
}); 

答案 3 :(得分: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)以允许异步操作。