Search code examples
reactjsreduxasync-awaitredux-thunk

Redux shorthand `mapDispatchToProps` returns undefined for `async` actions / thunks


I have encountered what I think might be a bug but could be my understanding.

Basically an import of an async action returns undefined inside a Redux container, but I know the import is fine (file exists, it's the right case etc etc).

On closer inspection, the issue seems to have started when I migrated to the shorthand object notation in my container, here's an example:

thunks.js

import { anAsyncFunc } from './anotherFile'
export const aThunk = () => async (dispatch, getState) => {
    await dispatch(anAsyncFunc)
    return dispatch(someOtherAsyncFunc)
}

containerThatDoesntWork.js

import { aThunk  } from './thunks.js'
import MyComponent from './MyComponent'

console.log(aThunk) // undefined

const mapDispatchToProps = {
    aThunk
}

containerThatDoesWork.js

import { aThunk  } from './thunks.js'
import MyComponent from './MyComponent'

console.log(aThunk) // undefined

const mapDispatchToProps = dispatch => ({
    aThunk: () => {
       console.log(aThunk) // return async function (dispatch, getState) { ... }
       return dispatch(aThunk())
    }
})

In both instances if I console.log(aThunk) in the container I get undefined which I presume is something to do with the module resolution of async functions?

However in the second example where the import is wrapped explicitly in a function, the component is happy and in the shorthand notation it is not (PropTypes validation fails).

Does anyone know a way round this? or is it a limitation to mapDispatchToProps in object shorthand notation?


Solution

  • const mapDispatchToProps = {
        aThunk
    }
    

    and

    const mapDispatchToProps = dispatch => ({
        aThunk: () => dispatch(aThunk())
    })
    

    are supposed to behave the same way. In case mapDispatchToProps is an object, properties are transformed to () => dispatch(action) automatically. Both should result in aThunk prop that returns the result of aThunk()(dispatch), i.e. a promise.

    Here is a demo.

    The only reason why named import can be undefined in module scope but exist in function scope is that there is circular dependency that was resolved at the time when a dependency was lazily accessed. Wrapping a dependency with a function like this was done with dispatch => ... is a known workaround around circular dependencies that is luckily supported by mapDispatchToProps, but it's preferable to not have them in the first place, especially if this isn't by design.