Next.js中Redux Toolkit服务端与客户端Store同步失败排查
核心问题分析
你当前的问题根源在于:
- 服务端单独创建的
store完成数据请求后,状态没有传递给客户端初始化的store,客户端拿到的是空白状态,导致RTK Query重新发起请求。 - 未处理Next.js的HYDRATE同步过程,服务端生成的RTK Query缓存无法同步到客户端。
针对Next.js App Router的解决方案
1. 配置RTK Query处理HYDRATE动作
修改reducer文件,添加HYDRATE逻辑,确保服务端缓存能正确合并到客户端:
import { combineSlices } from "@reduxjs/toolkit"; import { productApiSlice } from "./productApiSlice"; import { authApiSlice } from "./authApiSlice"; import { cartApiSlice } from "./cartApiSlice"; import { HYDRATE } from "next-redux-wrapper"; const reducer = (state, action) => { if (action.type === HYDRATE) { // 合并服务端与客户端状态,保留客户端pending请求 const nextState = { ...state, ...action.payload, [productApiSlice.reducerPath]: { ...state[productApiSlice.reducerPath], ...action.payload[productApiSlice.reducerPath], }, [authApiSlice.reducerPath]: { ...state[authApiSlice.reducerPath], ...action.payload[authApiSlice.reducerPath], }, [cartApiSlice.reducerPath]: { ...state[cartApiSlice.reducerPath], ...action.payload[cartApiSlice.reducerPath], }, }; return nextState; } // 正常处理其他动作 return combineSlices({ [authApiSlice.reducerPath]: authApiSlice.reducer, [productApiSlice.reducerPath]: productApiSlice.reducer, [cartApiSlice.reducerPath]: cartApiSlice.reducer, })(state, action); }; // 保持middleware配置不变 const middleware = (getDefaultMiddleware: () => any) => getDefaultMiddleware().concat( authApiSlice.middleware, productApiSlice.middleware, cartApiSlice.middleware, ); export { middleware, reducer };
2. 在根Layout中共享服务端Store状态
在app/layout.tsx(服务端组件)中创建store、预加载数据,并将状态传递给客户端Provider:
import { Provider } from "react-redux"; import { createStore } from "@/app/store/createStore"; import type { ReactNode } from "react"; import { productApiSlice } from "@/app/store/productApiSlice"; // 服务端预加载初始数据 async function getInitialServerState() { const store = createStore(); // 发起请求并等待完成,填充缓存 await store.dispatch(productApiSlice.endpoints.getProducts.initiate()).unwrap(); return store.getState(); } export default async function RootLayout({ children }: { children: ReactNode }) { const initialState = await getInitialServerState(); return ( <html lang="en"> <body> <Provider store={createStore(initialState)}> {children} </Provider> </body> </html> ); }
3. 客户端组件直接复用缓存
你的客户端组件代码无需修改,此时useGetProductsQuery会直接读取服务端预加载的缓存,不会重复发起请求:
const { data: products, error, isLoading } = useGetProductsQuery(); if (isLoading) return <p>Loading...</p>; if (error) return <p>Error fetching products</p>; return ( <tbody className="divide-y divide-gray-200"> {products?.map((product: Product, index: number) => ( <TableRow product={product} index={index} /> ))} </tbody> );
针对Next.js Page Router的解决方案(旧路由体系)
1. 配置next-redux-wrapper
修改createStore函数,适配next-redux-wrapper的状态同步:
import { configureStore } from "@reduxjs/toolkit"; import { reducer, middleware } from "./reducers"; import { createWrapper } from "next-redux-wrapper"; export const createStore = (preloadedState?: any) => { return configureStore({ preloadedState, reducer, middleware, }); }; export const wrapper = createWrapper(createStore, { debug: false });
2. 在页面中通过getServerSideProps预加载数据
在页面组件中,利用wrapper.getServerSideProps发起服务端请求:
import { wrapper } from "@/store/createStore"; import { productApiSlice } from "@/store/productApiSlice"; export const getServerSideProps = wrapper.getServerSideProps( async (context) => { const store = context.store; await store.dispatch(productApiSlice.endpoints.getProducts.initiate()).unwrap(); return { props: {} }; } ); // 页面组件内直接使用useGetProductsQuery即可
调试技巧
- 打印Store状态:服务端获取数据后,打印
store.getState(),确认productApi.queries.getProducts是否存在有效缓存。 - 客户端初始化校验:在客户端Provider初始化时,打印
createStore(initialState).getState(),确认服务端状态是否正确传递。 - Network面板排查:如果客户端仍发起请求,检查请求的缓存键是否与服务端一致(无参数的
getProducts键应完全相同)。 - HYDRATE动作日志:在reducer中添加
console.log(action),查看HYDRATE动作是否触发,以及payload是否包含服务端缓存数据。
内容的提问来源于stack exchange,提问作者Richard
相关产品推荐
相关产品推荐

