0

I am trying to make use of thunk to make async calls to api, but I am still getting the error : Unhandled Runtime Error: Actions must be plain objects. Use custom middleware for async actions.

This is my custom _app component:

// to connect redux with react 
import { Provider } from 'react-redux';
import { createWrapper } from 'next-redux-wrapper';

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

const store = createStore(reducers, applyMiddleware(thunk));

const AppComponent = ({ Component, pageProps }) => {
    return (
        <Provider store={store}>
            <Component {...pageProps} />
        </Provider>
    )
}

AppComponent.getInitialProps = async (appContext) => {
    let pageProps = {};
    if (appContext.Component.getInitialProps) {
        pageProps = await appContext.Component.getInitialProps(appContext.ctx);
    };
    return { ...pageProps }
}

// returns a new instance of store everytime its called
const makeStore = () => store;
const wrapper = createWrapper(makeStore);

export default wrapper.withRedux(AppComponent);

And this is the landing page where I am dispatching the action creator:

import { connect } from 'react-redux';
import { fetchPosts } from '../redux/actions';
import { bindActionCreators } from 'redux';
import { useEffect } from 'react';
import Link from 'next/link';

const LandingPage = (props) => {
    useEffect(() => {
        props.fetchPosts();
    }, [props]);

    return <div>
        <Link href="/">
            <a>Home</a>
        </Link>
    </div>
}

LandingPage.getInitialProps = async ({ store }) => {
    store.dispatch(await fetchPosts());
}

const mapDispatchToProps = (dispatch) => {
    return {
        // so that this can be called directly from client side
        fetchPosts: bindActionCreators(fetchPosts, dispatch)
    }
}


export default connect(null, mapDispatchToProps)(LandingPage);

Action:

import api from '../../api';

// returning a function and dispatching manually to make use of async await to fetch data
export const fetchPosts = async () => async (dispatch) => {
    const response = await api.get('/posts');
    dispatch({
        type: 'FETCH_POSTS',
        payload: response
    });
};

Sadly the GitHub Next + Redux example NEXT+REDUX is really complicated for me to understand as I am trying redux for the first time with NextJS. And every blog post has it's own way of doing it and nothing seems to be working.

I do not want it to make it any more complicated. I would really appreciate if anyone could help me why I am getting this error?

Karan Kumar
  • 1,012
  • 1
  • 4
  • 22

1 Answers1

0

the problem is not with next.js when you calling this :

LandingPage.getInitialProps = async ({ store }) => {
    store.dispatch(await fetchPosts());
}

fetchPosts here is a Promise and dispatch dispatch action must be a plain object so to solve this remove async word from it like this :

export const fetchPosts = () => async (dispatch) => {
    const response = await api.get('/posts');
    dispatch({
        type: 'FETCH_POSTS',
        payload: response
    });
};

butt if you want to wait for api response instead you need call it in the component like this :

const App= ()=>{
const dispatch = useDispatch()
useEffect(() => {
 const fetch = async()=>{
try{
    const response = await api.get('/posts');
    dispatch({
        type: 'FETCH_POSTS',
        payload: response
    });
}
catch(error){
throw error
}
 }
 fetch()
    }, []);

return ....
}
adel
  • 2,888
  • 1
  • 3
  • 13
  • That's why I am using Redux Thunk to avoid this. – Karan Kumar Aug 10 '20 at 10:18
  • yes redux thunk is middleware that intercept redux actions before they reach the reducer. they handle side effect but doesn't return a promise it's return a func call thunk which return action. but if you want to wait for the api check edited answer – adel Aug 10 '20 at 10:27
  • Can't I just get around with it and just dispatch the action inside the getInitialProps instead? That doesn't throw any error and I would NOT have to dispatch any action inside the useEffect or ComponentDidMount() . Because that would be dispatched at every render via GetInitialProps method. Right? – Karan Kumar Aug 10 '20 at 10:50
  • yes if your return the promise from action check this answer : https://stackoverflow.com/questions/54054887/redux-how-to-call-an-action-and-wait-until-it-is-resolved – adel Aug 10 '20 at 10:55