Next.js动态路由页面刷新后丢失问题求助
问题分析与解决方案
你的核心问题是页面刷新时Redux Store被重置,此时useSelector(selectDB)拿到的products是空数组,导致找不到匹配id的页面数据,一直显示loading。getServerSideProps仅传递了id,但没有在服务端完成数据预加载,无法支撑刷新后的初始渲染。
下面提供两种实用解决方案:
方案一:直接在服务端获取数据(不依赖Redux,最简便)
跳过Redux,直接在getServerSideProps中从Firestore获取对应产品数据,将数据直接传给组件。这样刷新时服务端会提前渲染好带内容的HTML,不会出现loading状态。
修改后的代码:
import { getFirestore, doc, getDoc } from 'firebase/firestore'; // 适配Firebase v9+语法 function Listing({ page }) { // 若服务端未获取到数据,返回404或loading if (!page) { return <p className="text-5xl">加载失败,产品不存在</p>; } return ( <div> <h1>{page.title}</h1> </div> ); } export default Listing; export async function getServerSideProps(context) { const { params } = context; const productId = params.id; // 从Firestore查询对应产品 const db = getFirestore(); // 注意:此处集合名和文档ID需与你的Firestore结构匹配 const productDoc = doc(db, 'products', productId); const docSnapshot = await getDoc(productDoc); // 处理产品不存在的情况 if (!docSnapshot.exists()) { return { notFound: true }; // 返回404页面 } // 将查询到的数据转为对象,传给组件 const productData = docSnapshot.data(); return { props: { page: productData, }, }; }
方案二:服务端预加载Redux数据(保留Redux依赖)
如果必须依赖Redux管理数据,需要在服务端初始化Redux Store并填充数据,确保客户端刷新后能拿到初始状态。
步骤1:配置_app.js处理服务端Redux初始化
import { Provider } from 'react-redux'; import { createStore } from 'redux'; import rootReducer from '../reducers'; // 你的根reducer function MyApp({ Component, pageProps, initialReduxState }) { // 用服务端传递的初始状态创建客户端store const store = createStore(rootReducer, initialReduxState); return ( <Provider store={store}> <Component {...pageProps} /> </Provider> ); } // 在_app层面获取服务端数据,统一管理Redux初始状态 MyApp.getInitialProps = async ({ Component, ctx }) => { const store = createStore(rootReducer); let pageProps = {}; // 调用页面的getServerSideProps,传入store用于dispatch if (Component.getServerSideProps) { pageProps = await Component.getServerSideProps(ctx, store); } return { pageProps, initialReduxState: store.getState(), // 将服务端store状态传给客户端 }; }; export default MyApp;
步骤2:修改页面的getServerSideProps填充Redux数据
import { getFirestore, doc, getDoc } from 'firebase/firestore'; import { useSelector } from 'react-redux'; import { setProduct } from '../actions'; // 你的Redux action function Listing({ id }) { const products = useSelector(selectDB); const page = products.find(x => x.title === id); if (!page) { return <p className="text-5xl">loading...</p>; } return ( <div> <h1>{page.title}</h1> </div> ); } export default Listing; // 接收store参数,用于dispatch数据 export async function getServerSideProps(context, store) { const { params } = context; const productId = params.id; // 从Firestore查询产品 const db = getFirestore(); const productDoc = doc(db, 'products', productId); const docSnapshot = await getDoc(productDoc); if (docSnapshot.exists()) { // 将数据dispatch到Redux store store.dispatch(setProduct(docSnapshot.data())); } return { props: { id: productId }, }; }
额外注意点
- 确认路由参数
id与Firestore中的字段匹配:如果路由id是文档ID而非title,则需要把x.title === id改为x.id === id(或对应文档ID的字段名),否则会一直找不到数据。
内容的提问来源于stack exchange,提问作者Billel Bahani
相关产品推荐
相关产品推荐

