Next.JS Redux dispatch not working in getStaticProps()

Viewed 1849

I am pretty new to Next.JS and I was trying to set up Redux with my Next.JS application. Now my page is supposed to display a list of posts that I am calling in from an API. The page renders perfectly when I'm dispatching from useEffect() to populate the data on to my page, but getStaticProps() or getServerSideProps() are not working whatsoever!

Here is a bit of code that will give you a hint of what I've done so far:

store.js

import { useMemo } from 'react'
import { createStore, applyMiddleware } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import thunk from 'redux-thunk'
import rootReducer from './reducers/rootReducer'

const initialState = {}
const middlewares = [thunk]

let store

function initStore(preloadedState = initialState) {
    return createStore(
        rootReducer,
        preloadedState,
        composeWithDevTools(applyMiddleware(...middlewares))
    )
}

export const initializeStore = (preloadedState) => {
    let _store = store ?? initStore(preloadedState)

    if (preloadedState && store) {
        _store = initStore({
            ...store.getState(),
            ...preloadedState,
        })
        store = undefined
    }

    if (typeof window === 'undefined') return _store
    if (!store) store = _store

    return _store
}

export function useStore(initialState) {
    const store = useMemo(() => initializeStore(initialState), [initialState])
    return store
}

action.js

export const fetchPosts = () => async dispatch => {
    const res = await axios.get('https://jsonplaceholder.typicode.com/posts')

    dispatch({
        type: FETCH_POSTS,
        payload: res.data
    })
}

_app.js

import { Provider } from 'react-redux'
import { createWrapper } from 'next-redux-wrapper'
import { useStore } from '../redux/store'

export default function MyApp({ Component, pageProps }) {
  const store = useStore(pageProps.initialReduxState)

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

These are the files that I needed for the basic redux setup. Once my store was set up and I wrapped my app around the Provider, I initially though of using useEffect() hook to populate data on a component that was rendering inside my index.js file.

component.js

import { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { fetchPosts } from '../redux/actions/postsAction'

const Posts = () => {
    const dispatch = useDispatch()
    const { items } = useSelector(state => state.posts)

    useEffect(() => {
        dispatch(fetchPosts())
    }, [])

    return (
        <div className="">
            <h1>Posts</h1>

            {items.map(post => {
                return (<div key={post.id}>
                    <h3>{post.title}</h3>
                    <p>{post.body}</p>
                </div>)
            })}
        </div>
    )
}

export default Posts

This worked perfectly! All my posts were showing up inside the component. The problem occurred when I was trying to achieve the same behaviour with server side rendering (or even SSG). I wanted to populate the data during the pre-render phase but for some reason the items array which is supposed to hold all the data is empty, basically meaning that the disptacher was never called! Here is the piece of code that is bothering me (exactly same as previous code, but this time I'm using getStaticProps() instead of useEffect()):

component.js

import { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { fetchPosts } from '../redux/actions/postsAction'

const Posts = ({ items }) => {
    return (
        <div className="">
            <h1>Posts</h1>

            {items.map(post => {
                return (<div key={post.id}>
                    <h3>{post.title}</h3>
                    <p>{post.body}</p>
                </div>)
            })}
        </div>
    )
}

export async function getStaticProps() {
    console.log('Props called')
    const dispatch = useDispatch()
    const { items } = useSelector(state => state.posts)

    dispatch(fetchPosts())
    console.log(items)

    return { props: { items } }
}

export default Posts

By running this, I'm getting an error that items is empty! Please help me, I have no clue what's going wrong here.

2 Answers

Well I fixed this issue myself but I forgot to post an answer for it, my bad!

The problem here really is very simple, hooks don't work outside of a functional component!

I think, inside of getStaticProps just call API or get datas from DB and returns it as props to pages/index.js (any component you want) and inside of this component we can get datas from getStaticProps as props.

Also we can set it as global state using useDispatch of react-redux. After that any component we can call those states using redux mapStateToProps. This is my solution.

Related