1
votes

I'm building a web app using next.js & redux, and trying to fetch data from getInitialProps on server side.

There is a redux action ready to be triggered in getInitialProps and it gets triggered when I hit or refresh the page.

The problem is that after it gets triggered data get fetched from DB and stored in redux store successfully but the stored data never move to the page props so I cannot use the fetched data at all.

If I check "store.getState()" after the action triggered, there are data fetched from DB.

Funny thing is, if I click a button that triggers the same action, data get fetched and move to the page props so I can use them.

What do I do wrong here and how can I send the stored data to the page props automatically in the first place?

import Head from 'next/head'
import Link from 'next/link'
import { connect } from 'react-redux'
import { getItems } from '../../store/actions/itemAction'

const Index = props => {
  return (
    <>
      <Head>
        <title>Item List</title>
      </Head>
      <div>
        <Button onClick={() => props.getItems()}>Get Items!</Button>
      </div>
    </>
  )
}

Index.getInitialProps = async ({ store }) => {
  await store.dispatch(getItems())
  return {}
}

const mapStateToProps = state => ({
  goods: state.item.goods,
})

const mapDispatchToProps = dispatch => ({
  getItems: () => dispatch(getItems())
})

export default connect(mapStateToProps, mapDispatchToProps)(Index)

Here is the itemReducer.js

import { GET_ITEMS } from '../types'

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

const itemReducer = (state = initialState, action) => {
  switch (action.type) {
    case GET_ITEMS:
      return {
        ...state,
        goods: action.payload,
        loading: false,
      }

    default:
      return state
  }
}

export default itemReducer
1
Why don't you access the store in initial props and return the goods in the object. Otherwise, you can try removing the await.Jose Felix
@Jose Felix Thank you for your comment. I know I can have access to the store in 'getInitialProps' and return the goods object in the end so that I can use the goods props in the page. But I would like to know why the goods props never moves to the page in the first place even though it is successfully stored in the state and mapped to props from mapStateToProps. If I get the goods object from getInitialProps, it means the mapStateToProps is not working and useless.Justin M
I believe, it is because you are returning an empty object. After it has gotten the state from Redux your getInitialProps returns an empty object, removing whatever you had. However, when you update the state through the button, you get the values through the connect without getInitialProps does overridiring them.Jose Felix
as far as I understood, you are saying that "props.goods" does not exist. If this is the case you should share your reducer to see what you are returningYilmaz
@Yilmaz Thanks for your concern. I've just added the itemReducer.js. What I can't figure out is that even though getItems() is dispatched in the Index.getInitialProps, and the fetched data are stored in global state, and the state is mapped to the page props through mapStateToProps, it turns out that props.item.goods in the page is empty. I can know the fetched data are stored right after the dispatched action because when I console.log store.getState() after the dispatched action in the getInitialProps, it shows that there are data stored in the `goods' array.Justin M

1 Answers

0
votes

First, getInitialProps() usually returns some page props. Then, did you know that mapStateToProps() has a second (optional) input parameter? See react-redux documentation.

const Index = ({ goods, getItems }) => (
  <>
    <Head>
      <title>Item List</title>
    </Head>
    <div>{JSON.stringify(goods)}</div>
    <div>
      <Button onClick={() => getItems()}>Get Items!</Button>
    </div>
  </>
);

Index.getInitialProps = async ({ store }) => {
  const goods = await store.dispatch(getItems());
  return { goods };
};

const mapStateToProps = (state, ownProps) => ({
  // At this point you could source your data either
  // from `state.goods` or `ownProps` (which now has a `goods` prop).
  // But sourcing it from the redux store means as soon as `goods`
  // changes in the redux store, your component will re-render with
  // the updated `goods`. A key aspect of `mapStateToProps()`.
  goods: ...state.goods
});

const mapDispatchToProps = dispatch => ({
  // I am assuming that dispatching this action will create 
  // a value in the redux store under the `goods` property
  getItems: () => dispatch(getItems())
});

export default connect(mapStateToProps, mapDispatchToProps)(Index);