🧠 Redux 番外篇 3:Redux + SSR 实战指南——Next.js 中的状态注入与脱水方案

76 阅读3分钟

✍️ Next.js 实战
🎯 目标:实现 Redux 在服务端渲染(SSR)中的安全注入、状态脱水与复用
🧠 关键词:Redux Toolkit、Next.js、SSR、hydrate、脱水/注水、状态隔离


🔍 为什么 SSR 场景下 Redux “容易出坑”?

在服务端渲染框架(如 Next.js)中:

  • 你要在服务端生成页面(含状态) → getServerSideProps
  • 再把状态注入给前端 → window.__PRELOADED_STATE__
  • 前端拿到这个 state 之后,还要接着使用 Redux…

这涉及 3 个问题:

  1. 🧠 服务端如何创建和使用 Redux store?
  2. 💾 如何将服务端生成的 state 注入给前端?
  3. 🔁 前端如何“接手”这个状态并复用?

✅ 实战一:服务端创建 Redux store + 获取初始状态

我们封装一个 makeStore() 工厂函数:

// store.ts
import { configureStore } from '@reduxjs/toolkit'
import rootReducer from './slices'

export const makeStore = (preloadedState = {}) => {
  return configureStore({
    reducer: rootReducer,
    preloadedState
  })
}

在 getServerSideProps 中使用:

// pages/index.tsx
import { makeStore } from '../store'

export const getServerSideProps = async () => {
  const store = makeStore()

  // 触发初始化 action
  store.dispatch(fetchUserData())
  store.dispatch(fetchPosts())

  return {
    props: {
      preloadedState: store.getState()
    }
  }
}

💧 实战二:脱水 + 注水机制(dehydrate → hydrate)

脱水(服务端生成状态对象)

return {
  props: {
    preloadedState: store.getState()
  }
}

注水(前端接管初始状态)

import { makeStore } from '../store'

export function useHydratedStore(preloadedState) {
  const storeRef = useRef<any>()
  if (!storeRef.current) {
    storeRef.current = makeStore(preloadedState)
  }
  return storeRef.current
}

🧪 实战三:Next.js 页面中接入 Redux 并复用状态

import { Provider } from 'react-redux'
import { useHydratedStore } from '../utils/useHydratedStore'

export default function Page({ preloadedState }) {
  const store = useHydratedStore(preloadedState)

  return (
    <Provider store={store}>
      <PageContent />
    </Provider>
  )
}

⚠️ 状态隔离问题与解决方案

问题:

服务端渲染每个请求都应拥有独立 Redux store,否则状态会跨用户污染。

正确做法:

  • 不要把 store 声明为单例
  • 使用 makeStore() 工厂函数
  • 服务端每次请求都重新调用 makeStore()

✅ 这样你每个请求都有自己的 store → 不会串数据、不泄露用户状态。


🎯 完整架构流程图

[Client Request][Next.js getServerSideProps][创建 store + dispatch 初始化 action][store.getState() → 脱水][传递给页面 props][前端 useHydratedStore(preloadedState)][Redux 正常运行 + 保持一致]

🧩 Redux Toolkit 的优势:无需再手写样板代码

使用 Redux Toolkit,可以:

  • 避免手动 combineReducers
  • 自动注入默认中间件(thunk、devtools)
  • 类型系统自动联动
  • 兼容 preloadedState 机制
const store = configureStore({
  reducer: { ... },
  preloadedState
})

🔐 推荐封装模式:高阶组件形式封装 Redux 注水

export function withRedux(Component) {
  return function Wrapper({ preloadedState, ...props }) {
    const store = useHydratedStore(preloadedState)
    return (
      <Provider store={store}>
        <Component {...props} />
      </Provider>
    )
  }
}

这样在任何 SSR 页面中都可以简洁使用:

export default withRedux(Page)

🧪 示例:用户登录数据 SSR 注入 Redux 流程

// getServerSideProps
const user = await fetchLoginStatus(ctx.req.cookies.token)
store.dispatch(setUser(user))

// 页面中使用 useSelector
const user = useSelector(state => state.user)
  • 服务端生成:store.dispatch(setUser())
  • 注水给前端:store.getState() → preloadedState
  • 前端 UI 立即感知 user,无需再发请求

🔚 总结:SSR 中正确使用 Redux 的核心理念

要素最佳实践
状态初始化位置getServerSideProps 中 dispatch
状态脱水方式store.getState()
注水方式preloadedStateconfigureStore
状态隔离每次请求都调用 makeStore()
UI 接入 Redux使用 <Provider store={...}> 包裹

⏭️ 下一篇预告

下一篇将聚焦 AI:

番外篇 4:《Redux + AI:状态生成、流转预测与智能助手实践》
你将学会:

  • 用 LLM 自动生成 reducer + action
  • 构建一个 ChatGPT 风格的“状态调试助手”
  • 使用状态流日志训练你的智能模型,预测用户操作路径