在客户端导航中保留状态值 - 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 之间的差异,然后只应用差异而不是整个初始存储。
- 获取客户端和服务器状态之间的差异。
- 制作下一个状态,用修补后的服务器状态覆盖客户端状态,这样这包括来自 hydrate 的更新状态和现有客户端状态。
- 当前生成白页。
您可以在 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);
};
所以我正在尝试解决我在使用 wrapper.getServerSideProps
时遇到的补水问题。当我使用当前设置重新路由时,存储被清除,然后添加新数据,这导致出现白页,因为许多重要数据不再存在(即翻译和 cms 数据)。
redux-dev-tools Hydrate 操作差异的屏幕截图:
屏幕截图是从主页路由到产品页面后截取的,因此有一个现有的商店。一切都重置为初始应用程序状态。
我想做什么
在 store.js 中,我创建了商店并预见到一个 reducer 来处理 Hydrate 调用。这种方法的缺点是有效载荷将始终是一个新的存储对象,因为它是在服务器上调用的。我想检查 2 json 之间的差异,然后只应用差异而不是整个初始存储。
- 获取客户端和服务器状态之间的差异。
- 制作下一个状态,用修补后的服务器状态覆盖客户端状态,这样这包括来自 hydrate 的更新状态和现有客户端状态。
- 当前生成白页。
您可以在 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);
};