Search code examples
reactjsexpressreduxmiddleware

Can anyone explain what does middleware does in this example https://github.com/erikras/react-redux-universal-hot-example


Can anyone explain what does middleware does in this example https://github.com/erikras/react-redux-universal-hot-example/ It does not support real api requests?

export default function clientMiddleware(client) {
return ({dispatch, getState}) => {
return next => action => {
  if (typeof action === 'function') {
    return action(dispatch, getState);
  }
  const { promise, types, ...rest } = action;
  if (!promise) {
    return next(action);
  }
  const [REQUEST, SUCCESS, FAILURE] = types;
  next({...rest, type: REQUEST});
  const actionPromise = promise(client);
  console.log('client ');
  console.log(client);
  actionPromise.then(       
    (result) => next({...rest, result, type: SUCCESS}),
    (error) => next({...rest, error, type: FAILURE})
  ).catch((error)=> {
    console.error('MIDDLEWARE ERROR:', error);
    next({...rest, error, type: FAILURE});
  });

  return actionPromise;
};
};
}

Solution

  • Before going to find out what the clientMiddleware does, you need to know the purposes of the following files:

    src/helpers/ApiClient.js: a class with a set of helper methods to perform API calls

    src/redux/create.js: this method receives an instance of ApiClient as parameter client this is the same parameter the clientMiddleware gets

    Now the clietMiddleware, the working is commented in the code

    // The clientMiddleware basically aims to intercept actions which performs API call
    // and it controls the dispatch of SUCCESS/FAILURE actions based on response
    export default function clientMiddleware(client) {
        return ({dispatch, getState}) => {
            return next => action => {
                // action creator is expected to return an object but
                // if we get a function instead, lets unwrap the top layer
                // by invoking the method and see if the action creator is inside
                if (typeof action === 'function') {
                  return action(dispatch, getState);
                }
    
                // destructure properties from action object
                const { promise, types, ...rest } = action;
    
                // the middleware can work only if the Object returned by
                // the action creator has a key 'promise' which is assigned
                // a function that returns promise (essentially your API call)
                if (!promise) {
                  // no 'promise' key? that means this action is not an API call
                  // let's pass this action to next middleware
                  return next(action);
                }
    
                // destructures elements from types array
                // REQUEST = types[0]
                // SUCCESS = types[1]
                // FAILURE = types[2]
                const [REQUEST, SUCCESS, FAILURE] = types;
    
                // Call next middleware with action.type = REQUEST
                // this will eventually get dipatched and the REQUEST reducer will get executed
                // Invokes a reducer just before the API call
                // a chance to set flags like 'loading': { ...state, loading: true }
                next({ ...rest, type: REQUEST });
    
                // as I said before 'promise' is a function we got by destructuring 'actions'
                // and that function receives instance of ApiClient as parameter
                // thus in turn a promise is returned and we are storing that to a variable here
                const actionPromise = promise(client);
    
                // as the name says, 'actionPromise'' is a JavaScipt promise, a 'then'able one.
                actionPromise.then(
                    // on succesfull completion of the API call dispatch the SUCCESS action with result
                    // from API response, any additional parameters required by the reducer goes in ...rest
                    (result) => next({ ...rest, result, type: SUCCESS }),
    
                    // similar to the success case, we dipatch the FAILURE action with error when API fails
                    (error) => next({ ...rest, error, type: FAILURE })
                ).catch((error)=> {
                    next({ ...rest, error, type: FAILURE });
                });
    
                return actionPromise;
            };
        };
    }