10

Why use Redux Thunk then one can do something like this:

ReadableAPI.getCategories().then((categories)=>{
      console.log('after getCategories', categories)

      this.props.dispatch(addCategories(categories))
    })

Isn't this more straightforward and achieves the same thing?

preston
  • 3,721
  • 6
  • 46
  • 78

3 Answers3

3

This answer from Dan Abramov explains beautifully why you would want to use redux-thunk in your application. One more benefit of using redux-thunk in your application is that you keep your business logic separate from your view part (React in your case). We had a use case where our app was written in backbone and we wanted to re-write our whole app in React. We realised that it is easy if your view and collection/models are separate. We started depreciating just the html templates and not the collections. When we deprecated all the html templates, we started using Redux in our app and deprecated our collections too.

What I want to put here is that we had our view and business logic separate, we could refactor that easily. Similarly, with React and Redux, you would want to keep that different, so that if something new comes and replaces Redux, at least you won't have to deprecate your views and you just will have to change your business logic.

Ajay Gaur
  • 5,140
  • 6
  • 38
  • 60
2

Redux Thunk basically allows us to delay the dispatch of an action, i.e we can handle the action returned by the action creator and call the dispatch function when we really want to dispatch.

Saptarshi Dey
  • 356
  • 1
  • 4
  • 13
0

Your example is incomplete and it was frustrating to follow how you arrived at that oversimplified solution. After researching it I realized, you probably have some ReadableAPI.js file somewhere you should have posted with what is probably a configuration using fetch and inside of it you probably have something like this:

export const getCategories = () =>
fetch('http://localhost:3001/categories', {headers})
  .then(res => res.json())
  .then(data => console.log(data))

which ties into your:

ReadableAPI.getCategories().then((categories)=>{
   console.log('after getCategories', categories)

   this.props.dispatch(addCategories(categories))
})

So in this solution you are returning a Promise which is an object which essentially gives us notification when some amount of work such as a network request is completed and in order to get notified we chain on the .then() function which we pass an arrow function like you did: then((categories)=> and that arrow function will be called at some point in the future.

It looks like you are referring to that data as categories and you are console logging 'after Categories', categories.

What we need to know is what are the different properties attached to that categories object? Does it have a data property? Does it have a results property with some actual data in it? Is there a categories.data.results that contains whatever the data is?

So let's just say the answer is yes to all the questions.

You are going about that in a bit of a hard way in order to deal with asynchronous requests because it's not just that snippet of code: there is also what's inside the ReadableAPI.js file, right? Also, you are using Promises that can get kind of hairy and you would have already put together two files just to deal with asynchronous request which would be okay if it was just a plain Reactjs application, but you mentioned your approach as an alternative to Redux-Thunk which implies using Redux.

For your approach in the vanilla Reactjs space I would use Axios and implement the async/await syntax, but with Redux involved you don't want to use a Promise.

Now, the action creator I had to make up in the ReadableAPI.js file would not work in a Redux environment because it does not return a plain JavaScript action object and so we would have to use a custom middleware as the error says to do.

So how does a middleware like Redux-Thunk work? Redux-Thunk essentially relaxes the rules around an action creator.

The purpose of Redux-Thunk is not to be passed a request object and it will take it away and go to work for you.

Redux-Thunk is an all purpose middleware that allows us to deal with asynchronous action creators, but it also allows us to do many other things as well.

With Redux Thunk involved, your action creator can return an action object. If you return an action object it still must have a type property and if it is an action object that gets returned it can optionally have a payload as well.

The other thing that Redux-Thunk does is allow you to return either an action object or a function.

If you return a function, Redux-Thunk will automatically call that function for you.

That's it, thats all Redux-Thunk does. However, One thing Redux-Thunk does really well is to manually dispatch an action. That is the key part. With Redux-Thunk we can manually dispatch an action at some point in time in the future.

So we get this new action created and it can be a plain JavaScript object or a function, but when we are dispatching it manually inside of Redux-Thunk or inside of a function it's basically always going to be a plain object.

So we will dispatch this action and it will flow back into dispatch and dispatch will send it right back into Redux-Thunk and Redux-Thunk will ask if it's an action or object.

When it's an object, Redux-Thunk forwards it automatically to all the different reducers.

With Redux-Thunk we can return a function and if we do, that function gets invoked with dispatch and getState arguments and with those two functions we have unlimited power over our Redux store and we can change any data and read any data and at any point in time in the future we can manually dispatch an action and update the data inside of our store.

Where am I getting the dispatch and getState? From the Redux-Thunk library source code: https://github.com/reduxjs/redux-thunk/blob/master/src/index.js

src/index.js:

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

    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

If you look at the if conditional you see the body of the actual logic that is going on. Did you just dispatch an action? If so, is it a function? If it is, then Redux-Thunk is going to invoke that action with dispatch and getState.

If our action is not a function, Redux-Thunk does not care about it, so on it goes to the next middleware as indicated by the return next(action);, otherwise on to the reducers if there is no middleware to run.

halfer
  • 19,824
  • 17
  • 99
  • 186
Daniel
  • 14,004
  • 16
  • 96
  • 156