next-redux-wrapper: Redux store not updating with SSR?

I’ve got a component where the store updates fine on the client side with the following code:

  componentDidMount() {
    const { loadQuoteIfNeededConnect, id } = this.props
    loadQuoteIfNeededConnect(id)
  }

If I comment that out and attempt to use the code below, the store does not update:

  static async getInitialProps({ store, query: { id } }) {
    // this fetches the data, but store is not updated
    await store.dispatch(loadQuoteIfNeeded(id))
    return { id }
  }

I’ve got a console.log directly above the return statement in my reducer, and I can see in my terminal that the data is in fact being fetched and returned properly with the code from getInitialProps(), but the store is not updating.

Any ideas?

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 35 (15 by maintainers)

Most upvoted comments

In my case it turns out I wasn’t returning the promise in my chain of dispatch functions. Working perfectly now!

For anyone else running into the same problem, here’s an example:

Component — using async/await

static async getInitialProps({ store, query: { id } }) {
    await store.dispatch(loadQuoteIfNeeded(id))
    return { id }
 }

Actions — note that loadQuoteIfNeeded() has an implicit return and getQuote() is being returned

export const initiateLoadQuote = quoteId => (dispatch) => {
  dispatch(loadQuote(quoteId))

  return getQuote(quoteId)
    .then(({ data: quotes }) => {
      if (quotes === null) Router.push('/')
      dispatch(loadQuoteSuccess(quotes, quoteId))
    })
    .catch((err) => {
      dispatch(loadQuoteError(quoteId))
      logAjaxError('initiateLoadQuote', err)
    })
}

export const loadQuoteIfNeeded = quoteId => (dispatch, getState) => (
  shouldLoadQuote(getState(), quoteId) ? dispatch(initiateLoadQuote(quoteId)) : null
)

I have same issue, im using nextjs + redux toolkit I got the updated store on server, but i didnt get the store on client up to date like on server

static async  getInitialProps(ctx) {
   if (ctx.isServer) {
     if (ctx.store.getState().users.categories.length === 0) {
        const res = await ctx.store.dispatch(users.usersSlice.actions.getCategories());
        console.log(ctx.store.getState());
        // that console.log return the new state of redux server store
      }
    } else {
      console.log('ctx', ctx.store.getState());
    }
    return {}
  }

Action

getCategories: () => async dispatch => {
    dispatch(categoriesReset());
    try {
      dispatch(signupLoading());
      const res = await axios.get(`/api/v1/users/categories`);
      dispatch(categoriesSucces(res.data));
      return res.data;
    } catch (err) {
      dispatch(categoriesError(err.response.data));
      return err;
    }
  }

Store

import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit';
import rootReducers from "./rootReducers";
const initStoreSSR = {
  users: {
    data: {},
    status: 0,
    token: "",
    message: '',
    categories: []
  }
};
export const initStore = (preloadedState = {}) => {
  return configureStore({
    reducer: rootReducers,
    preloadedState: { ...preloadedState, ...initStoreSSR },
    middleware: [...getDefaultMiddleware()]
  });
};

@brunoqs Looks like you’re overwriting your preloaded state with default state:

preloadedState: { ...preloadedState, ...initStoreSSR },

You need to change the order.

@jamal-rahimzadegan create an action and use something like redux-thunk or redux-promise-middleware to dispatch it.

@jamal-rahimzadegan

function getInitialProps = async ({store}) => {
   await postRequest(store);
}

without await you will create a detached promise chain which will be executed in parallel and won’t affect the resulting state that will be transferred to client before postRequest will finish. Also as a side note, you can’t use store anywhere outside Next.js lifecycle hooks and/or components.

@MSefer just follow the example: https://github.com/kirill-konshin/next-redux-wrapper#usage

import {createStore} from 'redux';

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

@kirill-konshin Thank you for response. I’m using version 6. I handled the Hydrate action. This solved my problem.

import { HYDRATE } from 'next-redux-wrapper';

const authReducer = (state = {}, action) => {
  switch (action.type) {
    case HYDRATE:
      return {...state, ...action.payload.authReducer};
    case 'authenticate':
      return { ...state, token: action.payload };
    case 'deauthenticate':
      return { ...state, token: null };
    case 'getUser':
      return { ...state, user: action.payload };
    default:
      return state;
  }
};

export default authReducer

I can’t help with MobX. Regarding redux you MUST explicitly tell NextJS to await until all actions are dispatched: https://github.com/kirill-konshin/next-redux-wrapper#async-actions-in-getinitialprops

Pay close attention that you won’t have any unhandled promises that were created inside the action.