Matt
Matt

Reputation: 47

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

I've read multiple sources about this error but I cannot figure out what I'm doing incorrectly here. I'm using custom middleware already and I believe that I'm returning the action correctly. Any advice?

app.js

import React from "react";
import ReactDOM from "react-dom";
import { renderToString } from "react-dom/server";
import { Provider } from "react-redux";
import { createStore, applyMiddleware, compose } from "redux";
import thunk from 'redux-thunk';

import rootReducer from '../reducers';
import DataProvider from "./DataProvider";
import QuestionContainer from "./QuestionContainer";
import * as actions from "../actions";

const App = () => <QuestionContainer />;
const store = createStore(
    rootReducer,
    applyMiddleware(thunk)),
);

store
    .dispatch(actions.fetchQuestions())
    .then(() => response.send(renderToString(<Provider store={ store }><App /></Provider>)))

Then in actions.js

export function fetchQuestions() {
    return (dispatch) => {
        return fetch('/api/questions')
            .then(response => response.json())
            .then(data => dispatch(loadRequestData(data)),
        )
    }
}

The error showing in browser console:

redux.js:208 Uncaught (in promise) Error: Actions must be plain objects. Use custom middleware for async actions.
    at dispatch (redux.js:208)
    at eval (index.js:12)
    at dispatch (redux.js:571)
    at eval (actions.js:35)

Upvotes: 0

Views: 390

Answers (1)

Szymon Pancerz
Szymon Pancerz

Reputation: 237

I think there's something's wrong in this part of code:

store
.dispatch(actions.fetchQuestions())
.then(() => response.send(renderToString(<Provider store={ store }><App /></Provider>)))

When you're creating async calls you want to do this only in action, not reducer/store.

So you need to delete this line .then(() => response.send(renderToString(<Provider store={ store }><App /> and instead of that just make:

const app = (
        <Provider store={store}>
            <App />
        </Provider>
)

ReactDOM.render(app, document.getElementById('root'));

Additionally make some actions which will be kind of helper for updating store in your reducer. Something like this:

export const fetchBegin = () => ({
    type: 'FETCH_BEGIN'
})

export const fetchSuccess = (payload) => ({
    type: 'FETCH_SUCCESS'
    payload
})

export const fetchQuestions = () => {
    return (dispatch) => {
        dispatch(fetchBegin())
        return fetch('/api/questions')
            .then(response => response.json())
            .then(data => dispatch(fetchSuccess(data))
        )
    }
}

Then in the reducers make:

const initialState = {
    call: [],
    loading: false
}

const reducer = (state = initialState, action){
    switch(action.type){
        case 'FETCH_BEGIN:
            return{
            ...state,
            loading: true,
        case 'FETCH_SUCCESS':
            return{
            ...state,
            call: action.payload,
            loading: false,
        }
        default:
            return state
    }
}

This should work imho.

Upvotes: 1

Related Questions