32
votes

I have a React App, I need to make an ajax call (in order to learn) to a online service (async) with Redux.

This is my store:

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import duedates from './reducers/duedates'


export default applyMiddleware(thunk)(createStore)(duedates);

This is the actions:

import rest from '../Utils/rest';

export function getDueDatesOptimistic(dueDates){
    console.log("FINISH FETCH");
    console.log(dueDates);
    return {
        type: 'getDueDate',
        dueDates
    }
}

export function waiting() {
    console.log("IN WAIT");
    return {
        type: 'waiting'
    }
}


function fetchDueDates() {
    console.log("IN FETCH");
    return rest({method: 'GET', path: '/api/dueDates'});
}

export function getDueDates(dispatch) {
    console.log("IN ACTION");
    return fetchDueDates().done(
        dueDates => dispatch(getDueDatesOptimistic(dueDates.entity._embedded.dueDates))
    )
}

And this is the reducer:

export default (state = {}, action) => {
  switch(action.type) {
    case 'getDueDate':
        console.log("IN REDUCER")

        return state.dueDates = action.dueDates;
    default:
        return state
  }
}

I dont get what I'm doing wrong. The action is being called perfectly from the component. But then I get this error:

Error: Actions must be plain objects. Use custom middleware for async actions.

I guess I'm using wrong the react-thunk middleware. What am I doing wrong?

EDIT

Now the action is calling to the reducer, but the reducer, after changing state, is not re-running the render method

    case 'getDueDate':
        console.log("IN REDUCER")

        return state.dueDates = action.dueDates;
3

3 Answers

30
votes

I think you should be using compose function, so it's like

import {
  createStore,
  applyMiddleware,
  compose
} from 'redux';
import thunk from 'redux-thunk';
import duedates from './reducers/duedates'

export default compose(applyMiddleware(thunk))(createStore)(duedates);

Thunk allows an action creator to return a function instead of plain-object, so you use it like

export function getDueDates() {
  return dispatch => {
    console.log("IN ACTION");
    fetchDueDates().done(
      dueDates => dispatch(getDueDatesOptimistic(dueDates.entity._embedded.dueDates))
    )
  };
}

You were returning a Promise object, that was one part of the problem. Another part was that redux-thunk hasn't been properly applied. I bet compose should get the problem solved.

17
votes

The accepted answer is either outdated, wrong or over-convoluted. Here are the docs on the compose subject:

http://redux.js.org/docs/api/compose.html

so we can do it like this instead:

import {createStore, combineReducers, compose, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';

const reducer = combineReducers({
    user: userReducer,
    items: itemsReducer
});


// here is our redux-store
const store = createStore(reducer,
    compose(applyMiddleware(thunk))
);
1
votes

I believe it is possible to have a working solution without using the compose function too. Based on the documentation from the GitHub repo for redux-thunk

Solution works for redux-thunk: ^2.0.0

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import duedates from './reducers/duedates'

export function configureStore(initialState = {}) {
  return createStore(
    duedates,
    initialState,
    applyMiddleware(thunk)
  );
}