You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 11:50:04