React-redux-universal-hot-example: Can anyone explain what does middleware does in this example

Created on 30 Nov 2016  路  1Comment  路  Source: erikras/react-redux-universal-hot-example

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

Most helpful comment

Ok I'll bite. Let's start from the basics.

This redux middleware, wired up inside createStore.js. It gets run everytime we invoke a redux action.

There are other middlewares as well that always gets run on each redux action. It's up to each middleware to determine whether or not it wants to respond to the action.

Now let's get to the code you asked about.

What is it trying to do overall?

The middleware executes an ajax request and runs a particular redux action if it is successful or another particular redux action if the ajax request fails.

The code for the middleware is looking for an action being invoked with an object that has the following properties:

  • types -- Expected to be an array of 3 objects. It assumes the first object is a request, the second object is a function to invoke when the request succeeds, and the third object is a function to invoke if the request fails.

  • promise -- a function to be executed that returns a promise

How does it do this? Let's walk through the code, I've added my comments as // Comment JDO:.

return ({dispatch, getState}) => {   ===> 

          **// Comment JDO: If the action is a function then please move on to the next action. This               
                                       middleware is looking for an object** 

          return next => action => {
                if (typeof action === 'function') {
                return action(dispatch, getState);
           }

          ** // Comment JDO:  If the action does not have a promise please move on to the next middleware. This middleware needs a promise to act on **
          const { promise, types, ...rest } = action;
          if (!promise) {
               return next(action);
          }

** // Comment JDO: This is where the real work takes place. An action has been invoked that has type  
                              being an object and has a property called promise. **

** // Comment JDO: The types property is expected to be an array. We use the first element as REQUEST, the second element as SUCCESS and the third element as FAILURE **

       const [REQUEST, SUCCESS, FAILURE] = types;

       next({...rest, type: REQUEST});

      ** // Comment JDO: Invoke the promise passing the apiClient. On success execute the SUCCESS function (second param of types above), If it returns failure then invoke the FAILURE function (third param of types above)**
       const actionPromise = promise(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;
    };
};

So let's look at how it's used. For instance in widgets.js

export function save(widget) {
  return {
    types: [SAVE, SAVE_SUCCESS, SAVE_FAIL],
    id: widget.id,
    promise: (client) => client.post('/widget/update', {
      data: widget
    })
  };
}

When you invoke the save action above, it passes an object to redux which gets passed to each middleware. The object has a types property, an id property and a promise property which takes in a client object.

If you remember our clientMiddleware discussion above, this is exactly the kind of object our middleware is coded to respond to.

It splits the types property into 3 parts:

  1. REQUEST == SAVE
  2. SUCCESS == SAVE_SUCCESS
  3. FAILURE == SAVE_FAIL

It then invokes the promise. In our case the promise is:

promise: (client) => client.post('/widget/update', {
      data: widget
    })

So this code causes a post request to be issued and if it succeeds it invokes the SAVE_SUCCESS action and if it fails it invokes the SAVE_FAIL action.

It's a lot to take in so feel free to ask questions.

>All comments

Ok I'll bite. Let's start from the basics.

This redux middleware, wired up inside createStore.js. It gets run everytime we invoke a redux action.

There are other middlewares as well that always gets run on each redux action. It's up to each middleware to determine whether or not it wants to respond to the action.

Now let's get to the code you asked about.

What is it trying to do overall?

The middleware executes an ajax request and runs a particular redux action if it is successful or another particular redux action if the ajax request fails.

The code for the middleware is looking for an action being invoked with an object that has the following properties:

  • types -- Expected to be an array of 3 objects. It assumes the first object is a request, the second object is a function to invoke when the request succeeds, and the third object is a function to invoke if the request fails.

  • promise -- a function to be executed that returns a promise

How does it do this? Let's walk through the code, I've added my comments as // Comment JDO:.

return ({dispatch, getState}) => {   ===> 

          **// Comment JDO: If the action is a function then please move on to the next action. This               
                                       middleware is looking for an object** 

          return next => action => {
                if (typeof action === 'function') {
                return action(dispatch, getState);
           }

          ** // Comment JDO:  If the action does not have a promise please move on to the next middleware. This middleware needs a promise to act on **
          const { promise, types, ...rest } = action;
          if (!promise) {
               return next(action);
          }

** // Comment JDO: This is where the real work takes place. An action has been invoked that has type  
                              being an object and has a property called promise. **

** // Comment JDO: The types property is expected to be an array. We use the first element as REQUEST, the second element as SUCCESS and the third element as FAILURE **

       const [REQUEST, SUCCESS, FAILURE] = types;

       next({...rest, type: REQUEST});

      ** // Comment JDO: Invoke the promise passing the apiClient. On success execute the SUCCESS function (second param of types above), If it returns failure then invoke the FAILURE function (third param of types above)**
       const actionPromise = promise(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;
    };
};

So let's look at how it's used. For instance in widgets.js

export function save(widget) {
  return {
    types: [SAVE, SAVE_SUCCESS, SAVE_FAIL],
    id: widget.id,
    promise: (client) => client.post('/widget/update', {
      data: widget
    })
  };
}

When you invoke the save action above, it passes an object to redux which gets passed to each middleware. The object has a types property, an id property and a promise property which takes in a client object.

If you remember our clientMiddleware discussion above, this is exactly the kind of object our middleware is coded to respond to.

It splits the types property into 3 parts:

  1. REQUEST == SAVE
  2. SUCCESS == SAVE_SUCCESS
  3. FAILURE == SAVE_FAIL

It then invokes the promise. In our case the promise is:

promise: (client) => client.post('/widget/update', {
      data: widget
    })

So this code causes a post request to be issued and if it succeeds it invokes the SAVE_SUCCESS action and if it fails it invokes the SAVE_FAIL action.

It's a lot to take in so feel free to ask questions.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ansonla3 picture ansonla3  路  4Comments

capir picture capir  路  4Comments

eromoe picture eromoe  路  5Comments

Dt-dev28 picture Dt-dev28  路  3Comments

massimopibiri picture massimopibiri  路  5Comments