如何在Next.js的SSG/SSR中使用Redux Toolkit的dispatch?
报错根本原因
useDispatch 是React专属Hook,仅能在函数组件体或自定义Hook内部调用,而getStaticProps/getServerSideProps是Next.js提供的、在服务端/构建阶段运行的普通Node.js函数,执行时没有React组件的运行上下文,自然会触发Hook调用规则报错,该问题和Redux本身无关,是Hook的运行限制导致的。
无第三方库的解决方案
核心逻辑非常简单:不要在服务端数据获取函数里调用Hook,直接手动实例化store、调用原生dispatch方法,再把预加载的state传递给客户端store做 hydration 即可。
第一步:编写独立的store生成函数
不要使用单例store,改为每次调用都返回全新store实例的函数,避免服务端多个请求共享状态导致污染:
// store/index.js import { configureStore } from '@reduxjs/toolkit' import counterReducer from './features/counterSlice' import userReducer from './features/userSlice' // 支持传入预加载state初始化 export const makeStore = (preloadedState) => { return configureStore({ reducer: { counter: counterReducer, user: userReducer }, preloadedState }) }
第二步:调整根组件的Provider适配hydration
修改_app.jsx(Pages Router)或根layout(App Router)的逻辑,让客户端store可以复用服务端生成的预加载state:
// pages/_app.jsx (Pages Router示例) import { Provider } from 'react-redux' import { makeStore } from '../store' // 缓存客户端的store实例,避免重复初始化 let clientStore const getClientStore = (preloadedState) => { // 服务端运行时每次返回新实例 if (typeof window === 'undefined') { return makeStore(preloadedState) } // 客户端运行时首次初始化用预加载state,后续复用现有实例 if (!clientStore) { clientStore = makeStore(preloadedState) } return clientStore } export default function MyApp({ Component, pageProps }) { const store = getClientStore(pageProps.preloadedReduxState) return ( <Provider store={store}> <Component {...pageProps} /> </Provider> ) }
第三步:在SSG/SSR函数中直接操作store
不需要调用useDispatch,直接实例化store后调用原生dispatch方法,再把预加载的state放到pageProps传递给客户端即可:
getServerSideProps示例:
// pages/user.jsx import { makeStore } from '../store' import { fetchUserList } from '../store/features/userSlice' export async function getServerSideProps() { // 生成当前请求专属的store实例 const store = makeStore() // 直接调用dispatch触发同步/异步action,支持await等待异步执行完成 await store.dispatch(fetchUserList()) // 将预加载的state传递给页面props return { props: { preloadedReduxState: store.getState() } } }
getStaticProps的用法完全一致,仅运行时机从每次用户请求改为构建阶段预执行。
注意事项
- 服务端每次执行
makeStore都会生成独立实例,不会出现不同用户请求的状态串扰问题 - 客户端hydration时直接复用服务端预拉取的状态,不需要二次请求已经获取过的数据
- 若使用Next.js 13+ App Router,只需将上述Provider逻辑封装为客户端组件,放到根layout中即可正常使用
内容的提问来源于stack exchange,提问作者Hasan
相关产品推荐
相关产品推荐

