在客户端导航中保留状态值 - NextJs - Next-Redux-Wrapper

Preserve state value on client side navigation - NextJs - Next-Redux-Wrapper

所以我正在尝试解决我在使用 wrapper.getServerSideProps 时遇到的补水问题。当我使用当前设置重新路由时,存储被清除,然后添加新数据,这导致出现白页,因为许多重要数据不再存在(即翻译和 cms 数据)。

redux-dev-tools Hydrate 操作差异的屏幕截图:

屏幕截图是从主页路由到产品页面后截取的,因此有一个现有的商店。一切都重置为初始应用程序状态。

我想做什么

在 store.js 中,我创建了商店并预见到一个 reducer 来处理 Hydrate 调用。这种方法的缺点是有效载荷将始终是一个新的存储对象,因为它是在服务器上调用的。我想检查 2 json 之间的差异,然后只应用差异而不是整个初始存储。

  1. 获取客户​​端和服务器状态之间的差异。
  2. 制作下一个状态,用修补后的服务器状态覆盖客户端状态,这样这包括来自 hydrate 的更新状态和现有客户端状态。
  3. 当前生成白页。

您可以在 store.js

中看到下面的 reducer 代码
//store.js

import combinedReducer from './reducer';

const bindMiddleware = (middleware) => {
    if (process.env.NODE_ENV !== 'production') {
        return composeWithDevTools(applyMiddleware(...middleware));
    }
    return applyMiddleware(...middleware);
};

const reducer = (state, action) => {
  if (action.type === HYDRATE) {
    const clientState = { ...state };
    const serverState = { ...action.payload };

    if (state) {
      // preserve state value on client side navigation

      // Get the difference between the client and server state.
      const diff = jsondiffpatch.diff(clientState, serverState);
      if (diff !== undefined) {
        // If there is a diff patch the serverState, with the existing diff
        jsondiffpatch.patch(serverState, diff);
      }
    }

    // Make next state, overwrite clientstate with patched serverstate
    const nextState = {
      ...clientState,
      ...serverState,
    };

    // Result, blank page.
    return nextState;
  }
  return combinedReducer(state, action);
};

export const makeStore = () => {
    const cookies = new Cookies();
    const client = new ApiClient(null, cookies);

    const middleware = [
        createMiddleware(client), 
        thunkMiddleware.withExtraArgument(cookies),
    ];

    return createStore(reducer, bindMiddleware(middleware));
};

const wrapper = createWrapper(makeStore);

export default wrapper;
//_app.jsx

const App = (props) => {
    const { Component, pageProps, router } = props;

    return (
        <AppComponent cookies={cookies} locale={router.locale} location={router}>
            <Component {...pageProps} />
        </AppComponent>
    );
};

App.getInitialProps = async ({ Component, ctx }) => {
    return {
        pageProps: {
            ...(Component.getInitialProps ? await Component.getInitialProps(ctx) : {}),
        },
    };
};

App.propTypes = {
    Component: PropTypes.objectOf(PropTypes.any).isRequired,
    pageProps: PropTypes.func,
    router: PropTypes.objectOf(PropTypes.any).isRequired,
};

App.defaultProps = {
    pageProps: () => null,
};

export default wrapper.withRedux(withRouter(App));
// Product page
export const getServerSideProps = wrapper.getServerSideProps(
async ({ query, store: { dispatch } }) => {
    const productCode = query.id?.split('-', 1).toString();
    await dispatch(getProductByCode(productCode, true));
});

const PDP = () => {
    const { product } = useSelector((state) => state.product);
    return (
        <PageLayout>
            <main>
                <h1>{product?.name}</h1>
                <div
                    className="description"
                    dangerouslySetInnerHTML={{ __html: product?.description }}
                />
            </main>
        </PageLayout>
    );
};

export default PDP;

好的,所以我通过不过度思考这个概念解决了我的问题。回到绘图板,做了一个简单的解决方案。

得出结论,只有少数状态对象需要在客户端导航期间保持不变。

我只需更改我的 i18n,使其动态化,因为我们是在页面基础上获取翻译的。

对于将来可能 运行 遇到类似问题的任何人来说,这是最终减速器。

const reducer = (state, action) => {
  if (action.type === HYDRATE) {
    const clientState = { ...state };
    const serverState = { ...action.payload };
    const nextState = { ...clientState, ...serverState };

    const locale = nextState.i18n.defaultLocale || config.i18n.defaultLocale;

    const nextI18n = {
      ...state.i18n,
      locale,
      messages: {
        [locale]: {
          ...state.i18n.messages[locale],
          ...nextState.i18n.messages[locale],
        },
      },
      loadedGroups: {
        ...state.i18n.loadedGroups,
        ...nextState.i18n.loadedGroups,
      },
    };

    if (state) {
      nextState.i18n = nextI18n;
      nextState.configuration.webConfig = state.configuration.webConfig;
      nextState.category.navigation = state.category.navigation;
    }

    return nextState;
  }
  return combinedReducer(state, action);
};