9

I have a serverless Next.js app in which there are some components that depend on data passed down from the redux store. Currently my method requires redux-persist to save the store as the user navigates, otherwise the aforementioned components will crash if the data from the store isn't present. The problem I'm experiencing is that redux-persist's PersistGate that is wrapped around the entire App is blocking outside requests to the site from getting access to the dynamic meta tags inside each of the pages. Tools like metatags.io only see the loading component from the persistgate.

If I remove the gate then the header tags will be rendered fine, but the redux state is no longer persistant and if the user refreshes the page on certain pages, it will cause issues.

I've tried some of the method's discussed in this github issue but conditionally wrapping the persistgate still causes issues & the 'next/head' component renders inside the actual body instead of the head.

I'm still quite new to Next.js & the SEO improvements it offered were the reasons I chose it over just plain React. I would be open to some suggestions of patterns that don't involve redux-persist & perhaps using localstorage.

Here is a layout of the _app.js as it currently stands:

import App from 'next/app';
// Redux:
import { useStore } from 'redux/store';
import { Provider } from 'react-redux';
import { persistStore } from 'redux-persist';
import { PersistGate } from 'redux-persist/integration/react';
import LoadingView from 'components/base/LoadingView';
import Layout from 'components/base/Layout';
// i18n wrapper:
import { appWithTranslation } from 'i18n';


function MyApp({ Component, pageProps }) {
  const store = useStore(pageProps.initialReduxState);
  const persistor = persistStore(store, {}, function persist() {
    persistor.persist();
  });

  return typeof window !== 'undefined' ? (
    <Provider store={store}>
      <PersistGate loading={<LoadingView />} persistor={persistor} onBeforeLift={() => new Promise(resolve => setTimeout(resolve, 1000))}>
            <Layout>
              <Component {...pageProps} />
            </Layout>
      </PersistGate>
    </Provider>
  ) : (
    <Provider store={store}>
          <Layout>
            <Component {...pageProps} />
          </Layout>
    </Provider>
  );
}

MyApp.getInitialProps = async appContext => {
  const appProps = await App.getInitialProps(appContext);

  return { ...appProps };
};

export default appWithTranslation(MyApp);

and this is the store with redux-persist:

// Taken from demo: https://github.com/vercel/next.js/blob/canary/examples/with-redux-persist/store.js
import { useMemo } from 'react';
import { createStore, applyMiddleware } from 'redux';
// Redux DevTools middleware:
import { composeWithDevTools } from 'redux-devtools-extension';
import reduxPromise from 'redux-promise';
// Persist Redux:
import { persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
// Main reducer:
import rootReducer from './reducers';

let store;

const persistConfig = {
  key: 'primary',
  storage
};

const persistedReducer = persistReducer(persistConfig, rootReducer);

export const makeStore = () => createStore(persistedReducer, composeWithDevTools(applyMiddleware(reduxPromise)));

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

  if (preloadedState && store) {
    _store = makeStore({
      ...store.getState(),
      ...preloadedState
    });

    store = undefined;
  }

  // For SSG and SSR always create a new store
  if (typeof window === 'undefined') return _store;
  // Create the store once in the client
  if (!store) store = _store;

  return _store;
};

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

Thanks in advance for any and all help.

HJEC
  • 400
  • 3
  • 11
  • 4
    Hey man!! did you find any solution? – Jawla May 29 '21 at 05:25
  • I ended up using [next-redux-cookie-wrapper](https://github.com/bjoluc/next-redux-cookie-wrapper) as a way of storing the most pertinent data via cookies which avoided the problem with SSR. The only thing to keep in mind is the size of the data you're storing, as cookie space is a lot more limited than localstorage. I noticed that once I'd saved two semi-large objects into redux, they wont be persisted. I hope that helps! – HJEC May 31 '21 at 09:54

0 Answers0