javascriptreduxnext.jsnext-redux-wrapper

How to dispatch an action from inside getInitialProps?


I am trying to implement Redux in a Next.js app and have problems getting the dispatch function to work in getInitialProps. The store is returned as undefined for some reason that I cannot figure out. I am using next-redux-wrapper. I have followed the documentation on next-redux-wrapper GitHub page but somewhere on the way it goes wrong. I know the code is working - I used axios to directly fetch the artPieces and then it worked just fine but I want to use Redux instead. I am changing an react/express.js app to a Next.js app where I will use the API for the basic server operations needed. This is just a small blog app.

Here is my store.js:

import { createStore } from 'redux';
import { createWrapper, HYDRATE } from 'next-redux-wrapper';

// create your reducer
const reducer = (state = { tick: 'init' }, action) => {
    switch (action.type) {
        case HYDRATE:
            return { ...state, ...action.payload };
        case 'TICK':
            return { ...state, tick: action.payload };
        default:
            return state;
    }
}; 

// create a makeStore function  
const makeStore = (context) => createStore(reducer);

// export an assembled wrapper
export const wrapper = createWrapper(makeStore, { debug: true });

And here is the _app.js:

import './styles/globals.css';
import { wrapper } from '../store';

function MyApp({ Component, pageProps }) {
    return <Component {...pageProps} />;
}

export default wrapper.withRedux(MyApp);

And finally here is where it does not work. Trying to call dispatch on the context to a sub component to _app.js:

import React from 'react';
import { ArtPiecesContainer } from './../components/ArtPiecesContainer';
import { useDispatch } from 'react-redux';
import axios from 'axios';
import { getArtPieces } from '../reducers';

const Art = ({ data, error }) => {
    return (
        <>
            <ArtPiecesContainer artPieces={data} />
        </>
    );
};

export default Art;

Art.getInitialProps = async ({ ctx }) => {
    await ctx.dispatch(getArtPieces());

    console.log('DATA FROM GETARTPIECES', data);

    return { data: ctx.getState() };
};

Solution

  • This should probably work with "next-redux-wrapper": "^7.0.5"

    _app.js

    import { wrapper } from '../store'
    import React from 'react';
    import App from 'next/app';
    
    class MyApp extends App {
      static getInitialProps = wrapper.getInitialAppProps(store => async ({Component, ctx}) => {
        return {
          pageProps: {
            // Call page-level getInitialProps
            // DON'T FORGET TO PROVIDE STORE TO PAGE
            ...(Component.getInitialProps ? await Component.getInitialProps({...ctx, store}) : {}),
            // Some custom thing for all pages
            pathname: ctx.pathname,
          },
        };
    
      });
    
      render() {
        const {Component, pageProps} = this.props;
    
        return (
          <Component {...pageProps} />
        );
      }
    }
    
    export default wrapper.withRedux(MyApp);
    

    and Index.js

    import { useEffect } from 'react'
    import { useDispatch } from 'react-redux'
    import { END } from 'redux-saga'
    import { wrapper } from '../store'
    import { loadData, startClock, tickClock } from '../actions'
    import Page from '../components/page'
    
    const Index = () => {
      const dispatch = useDispatch()
    
      useEffect(() => {
        dispatch(startClock())
      }, [dispatch])
    
      return <Page title="Index Page" linkTo="/other" NavigateTo="Other Page" />
    }
    
    Index.getInitialProps = wrapper.getInitialPageProps(store => async (props) => {
      store.dispatch(tickClock(false))
    
      if (!store.getState().placeholderData) {
        store.dispatch(loadData())
        store.dispatch(END)
      }
    
      await store.sagaTask.toPromise()
    });
    
    
    export default Index
    

    For the rest of the code you can refer to nextjs/examples/with-redux-saga, but now that I'm posting this answer they're using the older version on next-redux-wrapper ( version 6 ).