Next.js的getServerSideProps调用store.getState()返回空值问题
问题核心原因
getServerSideProps运行在服务端,执行时机远早于客户端组件的useEffect,仅在客户端useEffect中触发的listProductsaction从未在服务端执行,store默认初始状态下productList自然为空- 当前导出的是全局单例store,Node.js作为长期运行的服务进程,多个用户请求会共享同一个store实例,会出现不同用户数据交叉污染的严重问题
- 服务端发起网络请求时没有相对路径上下文,当前action里写的
/api/products在服务端运行时会直接报错,无法成功拿到数据 - 组件内直接打印
store.getState()为空是因为打印时机早于异步action执行完成的时机,此时state还未更新
修复步骤
1. 改造store为工厂函数
将原store.js的单例导出改为导出创建store的工厂函数,保证每次请求都生成独立的store实例:
import { combineReducers, createStore, applyMiddleware } from 'redux' import thunk from 'redux-thunk' import { composeWithDevTools } from 'redux-devtools-extension' // 引入你自己的reducer import { productListReducer, categoryListReducer } from './reducers' export const makeStore = (preloadedState = {}) => { const reducer = combineReducers({ productList: productListReducer, categoryList: categoryListReducer, }) const middleware = [thunk] return createStore( reducer, preloadedState, composeWithDevTools(applyMiddleware(...middleware)) ) }
2. 改造listProducts action兼容服务端请求
补全服务端请求的完整域名:
import axios from 'axios' export const listProducts = () => async (dispatch) => { try { dispatch({ type: 'PRODUCT_LIST_REQUEST' }) // 判断运行环境,服务端补全完整请求域名 const baseUrl = typeof window === 'undefined' ? 'http://localhost:3000' : '' const { data } = await axios.get(`${baseUrl}/api/products`) dispatch({ type: 'PRODUCT_LIST_SUCCESS', payload: data }) } catch (error) { dispatch({ type: 'PRODUCT_LIST_FAIL', payload: error.response && error.response.data.message ? error.response.data.message : error.message }) } }
3. 在getServerSideProps中手动触发异步action
在服务端创建store实例后先等待异步action执行完成,再读取state:
import { makeStore } from '../redux/store' import { listProducts } from '../redux/actions/productActions' export default function Home({products, error, loading}) { // 服务端已经返回数据,可直接使用,不需要再在useEffect中重复请求 return ( <> <Header /> <Products loading={loading} error={error} products={products} /> <Footer /> </> ) } export async function getServerSideProps() { // 每次请求新建独立store const store = makeStore() // 等待异步action执行完成 await store.dispatch(listProducts()) // 此时state已有数据 const { loading, error, products } = store.getState().productList return { props: { // 同时返回预加载状态,供客户端store初始化复用 preloadedState: store.getState(), products, loading, error } } }
4. 改造_app.js实现客户端store hydration
用服务端返回的预加载状态初始化客户端store,避免客户端重复发起请求:
import { Provider } from 'react-redux' import { makeStore } from '../redux/store' export default function MyApp({ Component, pageProps: { preloadedState, ...pageProps } }) { const store = makeStore(preloadedState) return ( <Provider store={store}> <Component {...pageProps} /> </Provider> ) }
内容的提问来源于stack exchange,提问作者shanksu 218
相关产品推荐
相关产品推荐

