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

如何在Next.js SSR中正确使用RTK Query?配置后SSR未生效

问题:Next.js + RTK Query SSR 预取数据不生效,客户端重复发起请求

我正在学习RTK Query,在Next.js中结合SSR机制使用时遇到了问题:按照官方文档配置后,服务器端虽然预取了数据,但客户端仍会重复发送请求,SSR未生效。以下是我的代码:

blog.tsx(博客页面)

import Layout from "@components/Templates/Layout"
import BlogsPage from "@components/Templates/BlogsPage"
import store from "@redux/store"
import { blogApi } from "@redux/api/blog"
import { useGetBlogPostsQuery } from "@redux/api/blog"

export default function Blog() {
  const { data, error, isLoading } = useGetBlogPostsQuery();

  return (
    <Layout>
      <BlogsPage
        posts={data.results}
      />
    </Layout>
  )
}

export const getServerSideProps = async () => {
  store.dispatch(blogApi.endpoints.getBlogPosts.initiate())

  await Promise.all(store.dispatch(blogApi.util.getRunningQueriesThunk()))
  return {
    props: {}
  };
}

store.ts

import { configureStore } from '@reduxjs/toolkit'
import { blogApi } from './api/blog';

const store = configureStore({
  reducer: {
    [blogApi.reducerPath]: blogApi.reducer,
  } as any,
  middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat([
    blogApi.middleware,
  ]),
})

export default store;

blogApi.ts

import { httpClient } from "./httpClient";

const injectedEndpoints = httpClient.injectEndpoints({
    endpoints: (builder) => ({

        getBlogPosts: builder.query<any, void>({
            query: () => ({
                url: `/blog/post/`,
                method: "GET"
            })
        }),

    })
})

export { injectedEndpoints as blogApi };
export const {
    useGetBlogPostsQuery,
} = injectedEndpoints;

httpClient.ts

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { HYDRATE } from 'next-redux-wrapper'

export const httpClient = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: process.env.NEXT_PUBLIC_API_URL+'api/v1' }),
  extractRehydrationInfo(action, { reducerPath }) {
    if (action.type === HYDRATE) {
      return action.payload[reducerPath]
    }
  },
  endpoints: () => ({}),
})

问题分析与解决方案

你遗漏了三个核心要点,导致SSR预取的数据无法被客户端复用:

1. 错误使用全局单例Store

Next.js SSR中每个请求都是独立的,全局单例Store会导致跨请求状态污染,同时客户端无法获取服务器预取的状态。必须将Store改为工厂函数,每个请求创建独立实例。

修改store.ts:

import { configureStore } from '@reduxjs/toolkit'
import { blogApi } from './api/blog';

// 改为工厂函数,每次调用创建新Store
export const makeStore = () => configureStore({
  reducer: {
    [blogApi.reducerPath]: blogApi.reducer,
  },
  middleware: (getDefaultMiddleware) => 
    getDefaultMiddleware().concat(blogApi.middleware),
})

// 导出类型方便后续使用
export type AppStore = ReturnType<typeof makeStore>
export type RootState = ReturnType<AppStore['getState']>

2. 未将服务器预取的状态传递给客户端

getServerSideProps需要将服务器Store的状态作为props返回,客户端才能基于该状态初始化Store,复用预取的数据。同时要使用RTK Query的initializeApiState方法将预取的缓存注入客户端Store。

修改blog.tsx:

import Layout from "@components/Templates/Layout"
import BlogsPage from "@components/Templates/BlogsPage"
import { makeStore } from "@redux/store"
import { blogApi } from "@redux/api/blog"
import { useGetBlogPostsQuery } from "@redux/api/blog"
import { Provider } from 'react-redux'

export default function Blog({ initialState }) {
  const store = makeStore()
  // 将服务器预取的RTK Query缓存注入客户端Store
  store.dispatch(blogApi.util.initializeApiState(initialState[blogApi.reducerPath]))

  // 此时useGetBlogPostsQuery会自动读取缓存,不会重复请求
  const { data, isLoading } = useGetBlogPostsQuery()

  return (
    <Provider store={store}>
      <Layout>
        <BlogsPage
          posts={data?.results || []}
          isLoading={isLoading}
        />
      </Layout>
    </Provider>
  )
}

export const getServerSideProps = async () => {
  // 为当前请求创建独立Store
  const store = makeStore()
  // 发起请求并等待完成
  const requestPromise = store.dispatch(blogApi.endpoints.getBlogPosts.initiate())
  await requestPromise.unwrap()

  return {
    props: {
      // 将服务器Store的状态传递给客户端
      initialState: store.getState()
    }
  };
}

3. 优化:确保客户端不重复请求

通过initializeApiState注入缓存后,RTK Query的useGetBlogPostsQuery会自动读取缓存,无需额外配置。如果需要强制控制,可以通过skip参数跳过请求:

const { data, isLoading } = useGetBlogPostsQuery(undefined, {
  skip: !!initialState[blogApi.reducerPath].queries['getBlogPosts(undefined)']
})

内容的提问来源于stack exchange,提问作者NavidMnzh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 21:54:53