10

I am trying to dispatch an action. I found working examples for some actions, but not as complex as mine.

Would you give me a hint? What am I doing wrong?

I am using TypeScript and have recently removed all typings and simplified my code as much as possible.

I am using redux-thunk and redux-promise, like this:

import { save } from 'redux-localstorage-simple';
import thunkMiddleware from 'redux-thunk';
import promiseMiddleware from 'redux-promise';

const middlewares = [
        save(),
        thunkMiddleware,
        promiseMiddleware,
    ];
const store = createStore(
        rootReducer(appReducer),
        initialState,
        compose(
            applyMiddleware(...middlewares),
            window['__REDUX_DEVTOOLS_EXTENSION__'] ? window['__REDUX_DEVTOOLS_EXTENSION__']() : f => f,
        ),
    );

Component - Foo Component:

import actionFoo from 'js/actions/actionFoo';
import React, { Component } from 'react';
import { connect } from 'react-redux';

class Foo {
    constructor(props) {
        super(props);
        this._handleSubmit = this._handleSubmit.bind(this);
    }
    _handleSubmit(e) {
        e.preventDefault();
        this.props.doActionFoo().then(() => {
            // this.props.doActionFoo returns undefined
        });
    }
    render() {
        return <div onClick={this._handleSubmit}/>;
    }
}

const mapStateToProps = ({}) => ({});

const mapDispatchToProps = {
    doActionFoo: actionFoo,
};

export { Foo as PureComponent };
export default connect(mapStateToProps, mapDispatchToProps)(Foo);

Action - actionFoo:

export default () => authCall({
    types: ['REQUEST', 'SUCCESS', 'FAILURE'],
    endpoint: `/route/foo/bar`,
    method: 'POST',
    shouldFetch: state => true,
    body: {},
});

Action - AuthCall:

// extremly simplified
export default (options) => (dispatch, getState) => dispatch(apiCall(options));

Action - ApiCall:

export default (options) => (dispatch, getState) => {
    const { endpoint, shouldFetch, types } = options;

    if (shouldFetch && !shouldFetch(getState())) return Promise.resolve();

    let response;
    let payload;

    dispatch({
        type: types[0],
    });

    return fetch(endpoint, options)
        .then((res) => {
            response = res;
            return res.json();
        })
        .then((json) => {
            payload = json;

            if (response.ok) {
                return dispatch({
                    response,
                    type: types[1],
                });
            }
            return dispatch({
                response,
                type: types[2],
            });
        })
        .catch(err => dispatch({
            response,
            type: types[2],
        }));
};
dazlious
  • 546
  • 6
  • 23
  • what does `console.log(res.json())` return? – random-forest-cat Jan 04 '18 at 21:52
  • It's a Promise. A resolved one – dazlious Jan 05 '18 at 08:52
  • Does your debugger tell you which `then` is being called on `undefined`? – Joshua R. Jan 05 '18 at 09:34
  • It's been a while since i've used redux thunk and i'm not sure but my guess is that actionFoo should return a dispatch call instead of a direct call to the authCall action. Maybe this example (which a wrote long ago) can help you: https://github.com/svenvandescheur/react-redux-consumerjs-example/ – Sven van de Scheur Jan 05 '18 at 09:40
  • @JoshuaR. Sure, the one: this.props.doActionFoo(gameName, password).then() I have commented in code above – dazlious Jan 05 '18 at 09:56
  • Do you have `redux-promise`? `redux-thunk` doesnt handle promises – The Reason Jan 05 '18 at 10:00
  • @SvenvandeScheur: No, because above is the short form of: const mapDispatchToProps = dispatch => { return { doActionFoo: () => dispatch(actionFoo()), }; }; – dazlious Jan 05 '18 at 10:00
  • @TheReason: I thought redux-thunk is handling this for me: https://github.com/gaearon/redux-thunk#composition – dazlious Jan 05 '18 at 10:02
  • @ShubhamKhatri: I do not need to try this, because arrow functions automatically return the next statement, if there is no block scope starting – dazlious Jan 08 '18 at 08:40
  • `AuthCall` is returning dispatch which does not return a promise. I don't follow why `AuthCall` needs to call `dispatch()` with `ApiCall` nested inside, if `ApiCall` calls dispatch – Eric Hasselbring Jan 09 '18 at 20:35

2 Answers2

5

From redux-thunk

Redux Thunk middleware allows you to write action creators that return a function instead of an action

So it means that it doesn't handle your promises. You have to add redux-promise for promise supporting

The default export is a middleware function. If it receives a promise, it will dispatch the resolved value of the promise. It will not dispatch anything if the promise rejects.

The differences between redux-thunk vs redux-promise you can read here

The Reason
  • 7,705
  • 4
  • 24
  • 42
4

Okay, after several hours, I found a solution. redux-thunk had to go first before any other middleware. Because middleware is called from right to left, redux-thunk return is last in chain and therefore returns the Promise.

import thunkMiddleware from 'redux-thunk';

const middlewares = [
        thunkMiddleware,
        // ANY OTHER MIDDLEWARE,
    ];
const store = createStore(
        rootReducer(appReducer),
        initialState,
        compose(
            applyMiddleware(...middlewares),
            window['__REDUX_DEVTOOLS_EXTENSION__'] ? window['__REDUX_DEVTOOLS_EXTENSION__']() : f => f,
        ),
    );
dazlious
  • 546
  • 6
  • 23