Ad

Redux: Using Async Middlewares Vs Dispatching Actions On Success Functions

- 1 answer

I am trying to integrate Redux into my React project. Currently I'm not using any Flux framework.

My app gets some data from the API and displays it in a pretty way, like so:

componentDidMount() {
  getData();
}

getData() {
  const self = this;

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

In reading about Redux, I've settled on two possible approaches that I could use for handling storing my success data in the store:

  • Use async middlewares, or
  • Dispatching action ADD_DATA from the success callback of the ajax function

But I'm unsure which is the better approach.

Dispatching action in callback sounds easy to implement and understand, while async middlewares are harder to explain to people who are not used to working with a functional language.

Ad

Answer

I personally prefer using custom middleware to accomplish this. It makes the actions a little easier to follow and has less boilerplate IMO.

I've set up my middleware to look for an object returned from a action that matches a certain signature. If this object schema is found, it handles it specially.

For example, I use an action that looks like this:

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

My custom middleware sees that the object has a types array and a promise function and handles it specially. Here's what it looks like:

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

I personally like this approach because it centralizes a lot of the logic and gives you a standard enforcement of how api actions are structured. The downside to this is that it could be a little magical to those who aren't familiar with redux. I also use thunk middleware also and both of these together solve all my needs so far.

Ad
source: stackoverflow.com
Ad