如何在Next.js App目录项目中用SSR数据初始化Redux Store
基于Next.js App目录结构用SSR数据初始化Redux Store的方案
核心思路:服务端拉取数据后,创建Redux Store时将数据作为初始状态传入,客户端hydrate阶段复用该状态,避免重复请求。以下是具体实现步骤:
1. 改造store.ts,支持传入初始状态
修改makeStore函数,允许接收预加载状态,以此初始化Store:
import { configureStore } from '@reduxjs/toolkit'; import authReducer from './feature/auth/authSlice'; import blogsReducer from './feature/blogs/blogFetchSlice'; import userReducer from './feature/user/userSlice'; // 新增preloadedState参数,类型支持RootState的部分字段 export const makeStore = (preloadedState?: Partial<RootState>) => { return configureStore({ reducer: { auth: authReducer, user: userReducer, blogs: blogsReducer, }, preloadedState // 注入初始状态 }); } export type AppStore = ReturnType<typeof makeStore>; export type RootState = ReturnType<AppStore['getState']>; export type AppDispatch = AppStore['dispatch'];
2. 在blogFetchSlice.tsx中添加初始化数据的Reducer
新增同步Action,用于设置服务端获取的博客数据:
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; import { BlogFetchAllState } from './types'; import { BlogGetData } from '../../models/BlogGet'; const blogFetchSlice = createSlice({ name: 'blogFetchAll', initialState: { blogs: null, loading: false, error: null, current_blog: null } as BlogFetchAllState, reducers: { // 同步Action:设置初始博客数据 setInitialBlogs: (state, action: { payload: BlogGetData[] }) => { state.blogs = action.payload; state.loading = false; state.error = null; } }, extraReducers: (builder) => { // 保留原有异步请求处理逻辑(若存在) // 示例:builder.addCase(fetchAllBlogs.pending, (state) => { ... }) } }); export const { setInitialBlogs } = blogFetchSlice.actions; export default blogFetchSlice.reducer;
3. 修改StoreProvider.tsx,支持接收初始状态
更新StoreProvider组件,使其能接收初始状态并初始化Store实例:
'use client'; // 标记为客户端组件(依赖React Hooks与Redux Provider) import { useRef } from 'react'; import { Provider } from 'react-redux'; import { AppStore, makeStore, RootState } from '../lib/store'; interface StoreProviderProps { children: React.ReactNode; initialState?: Partial<RootState>; } const StoreProvider = ({ children, initialState }: StoreProviderProps) => { const storeRef = useRef<AppStore>(); if (!storeRef.current) { // 用传入的初始状态创建Store storeRef.current = makeStore(initialState); } return <Provider store={storeRef.current}>{children}</Provider>; }; export default StoreProvider;
4. 在Home.tsx中传递服务端数据给StoreProvider
利用服务端组件的特性拉取数据,构造Redux初始状态并传给StoreProvider:
import { Box, Button, Typography } from "@mui/material"; import blogsecure from "./services/blogSecure"; import { BlogGetData } from "./models/BlogGet"; import StoreProvider from './StoreProvider'; export const fetchData = async () => { const response = await blogsecure.get('/blogs/fetch_all'); if (response.status !== 200) { throw new Error("Failed to fetch API data"); } return response.data; }; const Home = async() => { const blogs: BlogGetData[] = await fetchData(); // 构造匹配Redux状态结构的初始数据 const initialState = { blogs: { blogs: blogs, loading: false, error: null } }; return ( <StoreProvider initialState={initialState}> <Box sx={{ position: 'relative', height: '100%' }}> <Button sx={{ position: 'absolute', bottom: 16, right: 16 }}> Create blog </Button> {/* 可直接渲染数据,也可通过客户端组件从Redux取数 */} {blogs.map((blog) => <Typography key={blog.id}>{blog.title}</Typography>)} </Box> </StoreProvider> ); } export default Home;
可选:客户端组件从Redux取数示例
创建客户端组件BlogList.tsx,直接从Redux Store获取初始化数据:
'use client'; import { useSelector } from 'react-redux'; import { RootState } from '../lib/store'; import { Typography } from '@mui/material'; const BlogList = () => { const { blogs } = useSelector((state: RootState) => state.blogs); return ( <> {blogs?.map((blog) => <Typography key={blog.id}>{blog.title}</Typography>)} </> ); }; export default BlogList;
在Home.tsx中替换原渲染逻辑为<BlogList />即可。
内容的提问来源于stack exchange,提问作者Shivank Anchal
相关产品推荐
相关产品推荐

