如何在 Next.js 中持久化 redux 状态而不阻塞 SSR?

时间:2021-02-24 15:09:43

标签: redux next.js server-side-rendering meta-tags redux-persist

我有一个无服务器 Next.js 应用程序,其中有一些组件依赖于从 redux 存储传递的数据。目前我的方法需要 redux-persist 在用户导航时保存商店,否则如果来自商店的数据不存在,上述组件将崩溃。我遇到的问题是围绕整个应用程序的 redux-persist 的 PersistGate 正在阻止对站点的外部请求访问每个页面内的动态元标记。像 metatags.io 这样的工具只能看到来自persistgate 的加载组件。

如果我删除了门,那么标题标签将呈现良好,但 redux 状态不再持久,如果用户在某些页面上刷新页面,则会导致问题。

我已经尝试了 this github issue 中讨论的一些方法,但有条件地包装persistgate 仍然会导致问题,并且“next/head”组件呈现在实际主体内部而不是头部内部。

我对 Next.js 还是很陌生,它提供的 SEO 改进是我选择它而不是简单的 React 的原因。我愿意接受一些不涉及 redux-persist 和可能使用 localstorage 的模式建议。

这是 _app.js 目前的布局:

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);

这是带有 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]);
}

预先感谢您提供的任何帮助。

0 个答案:

没有答案