3

Redux-thunk allows you to create action-creators that return a function instead of an action. The inner function receives the store methods dispatch and getState as parameters.

function incrementAsync() {
  return (dispatch, getState) => {
    setTimeout(() => {
       dispatch(increment());
    }, 1000);
  };
}

But at the same time, react-redux' connect already has a mapDispatchToProps argument that can be used to wrap action creator into a dispatch call so they may be invoked directly. With mapDispatchToProps you can already do,

const mapDispatchToProps = (dispatch) => ({
  incrementAsync: () => {
    setTimeout(() => {
      dispatch(increment());
    }, 1000);
  }
});
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)

In my eyes, the same can be accomplished without redux-thunk. Why do we have the redux-thunk library in the first place? I'm sure I'm just failing to see it since redux-thunk is a fairly popular library.

Brandon Culley
  • 5,219
  • 1
  • 28
  • 28
turntwo
  • 2,452
  • 19
  • 28

5 Answers5

0

In short your solution is totally fine.

I think the convenience of using thunk middleware would be that you could treat any action creator call async or not the same way, meaning you don't have to pass the dispatch function around. Whenever what you dispatch is a function dispatch is injected into its returning function. To sum up, using redux-thunk makes action creators composable.

See Dan Abramovs detailed answer here: How to dispatch a Redux action with a timeout?

Also just looking at the redux-thunk code helped me understand this.

heyhugo
  • 1,015
  • 9
  • 14
0

It's true that you can access dispatch from your action this way but redux-thunk makes it possible without that wrapper function and explicit dispatch handoff.

You also get immediate access to the state from your action through the getState() function provided as the second parameter.

// actionFile.js
export someAction(dispatch) {
  return function(value) {
    dispatch({ type: 'SOME_TYPE', value });
  }
}

// component.jsx
const mapDispatchToProps = (dispatch) => ({
  someAction: someAction(dispatch)
});

vs.

// actionFile.js
export someAction(value) {
  return function(dispatch, getState) {
    dispatch({ type: 'SOME_TYPE', value });
    // maybe do some stuff with getState()
  }
}

// component.jsx
const mapDispatchToProps = { someAction };

It's definitely optional, but a lot cleaner in my opinion, and most importantly provides a better separation of concerns between your component and action creators. I.e., you don't need to fuss with how you define your mapDispatchToProps when at some point down the line you need to dispatch asynchronously or trigger side effects.

Drazen Bjelovuk
  • 5,201
  • 5
  • 37
  • 64
0

Aside from Redux Thunk, there are two big advantages to the first example:

  1. It's easier to test as it's isolated and functional, so you just need to include it and use in order to assert it behaves correctly (no prop querying, simulating or problems with shallow rendering that you'll have if it's in a component)
  2. It's possible to re-use it (the second example is impossible to re-use). Imagine you have to dispatch the same action from several places in the app and use the second pattern, duplicating the code, then the requirements for the action change?
Toni Leigh
  • 4,830
  • 3
  • 22
  • 36
  • `dispatch => () => { setTimeout(() => { dispatch(increment()); }, 1000); }` seems perfectly capable of being abstracted into a resuable function, used as `const mapDispatchToProps = (dispatch) => ({ incrementAsync: incrementAsync(dispatch) });`. – Drew Reese Jan 04 '23 at 07:56
  • @DrewReese I'd posit _anything_ could be re-written to be re-usable (perhaps there's a conjecture or proof about that), but I was referring to the code in the question as it is written – Toni Leigh Apr 25 '23 at 14:15
  • Ok, but in your answer you say "the second example is impossible to re-use". I'm simply disagreeing with you that this is an absolute truth. That you *can* abstract the second into a function invalidates your first point. Thunks don't exist to solve a problem of code testability/reusability. I don't recall how I stumbled onto this entire post back in January, but looking at it again now this is all outdated. We don't write ***new*** verbose legacy redux code that still uses the `connect` HOC anymore now with the `useDispatch` and `useSelector` hooks. – Drew Reese Apr 25 '23 at 15:46
  • @DrewReese my answer compares the two examples of code in the question, defining advantages of the first over the second _exactly_ as they are written, not as they _could_ be written. I feel this is closer answer to the question than re-writing the code in it, though such efforts are of value and may be better in an answer of their own, especially given how out of date my answer now is. – Toni Leigh Apr 26 '23 at 16:06
0

Action creators can be synchronous or asynchronous. redux-thunk is mainly used to create asynchronous action creators like sending an API call, in which an asynchronous action creator dispatches another function/action creator instead of returning just an action. The inner function receives store methods dispatch and getState as parameters.

  1. Wrapping the action creator into a dispatch call in mapDispatchToProps does not provide access to getState method. redux-thunk provides access to both, dispatch as well as getState.

    export const actionCreator = () => {
        return (dispatch, getState) => {
        //axios call
        dispatch(anotherActionCreator());
      };
    };
    
  2. Using action creator in mapDispatchToProps conflicts with encapsulation and consistency of using action creators as the component doesn't have to know about the details of creating and dispatching the action whether its a simple return of an action object or a complex redux-thunk asynchronous action creator.

  3. Creating action creators separately helps us to reuse the action creator at multiple places.

Hetal Rachh
  • 1,393
  • 1
  • 17
  • 23
0

One big advantage of using redux-thunk is being able to access the entire state when determining the action, something that is impossible to do using only mapDispatchToProps.

It may seem weird that you can't just get the whole state in mapDispatchToProps in the first place, but it makes sense since mapDispatchToProps is probably only called once on first render, and thus any state passed to it might be stale by the time one of the actions gets called.

So you can either use redux-thunk, or instead pass the entire pieces of state you need to the component as props to determine your action, which in some situations might be useless to render your component, thus triggering unnecessary and possibly costly re-renders, just so your component can work as a bridge to pass data to the action creator.