如何在Next.js中从index.tsx向其他页面传递状态变量?
从React路由迁移到Next.js:跨页面传递状态变量的方案
问题背景
我有一个React应用,其App.tsx中的代码如下:
function App() { let date = new Date(); // storing full name of all months in array const months = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", ]; const [currentMonth, SetCurrentMonth] = useState<string>( months[date.getMonth()] ); const [currentYear, SetCurrentYear] = useState<number>(date.getFullYear()); const changeMonth = (val: string) => { // if clicked icon is previous icon then decrement current month by 1 else increment it by 1 if (val === "prev") { if (currentMonth === "January") { SetCurrentMonth(months[11]); SetCurrentYear((currentYear) => currentYear - 1); } else { SetCurrentMonth(months[months.indexOf(currentMonth) - 1]); } } else { if (currentMonth === "December") { SetCurrentMonth(months[0]); SetCurrentYear((currentYear) => currentYear + 1); } else { SetCurrentMonth(months[months.indexOf(currentMonth) + 1]); } } }; return ( <div className="App"> <Routes> <Route path="/" element={ <> <ViewSelector /> <Summary monthNumber={months.indexOf(currentMonth)} month={currentMonth} year={currentYear} onClick={changeMonth} /> </> } /> <Route path="/calendar/:player" element={ <> <ViewSelector /> <Calendar date={date} monthNumber={months.indexOf(currentMonth)} month={currentMonth} year={currentYear} onClick={changeMonth} /> </> } /> <Route path="/manage" element={ <> <ViewSelector /> <Manage /> </> } /> </Routes> </div> ); } export default App;
在React中可以通过Routes和Route直接将currentMonth、currentYear等状态变量传递给路由组件,但迁移到Next.js的文件系统路由后,不知道如何从主入口向各页面文件夹下的独立index.tsx传递这些状态。
解决方案
Next.js的文件系统路由模式下,无法像React Router那样直接通过路由组件props传状态,以下是三种常用的可行方案:
1. React Context全局状态管理
这是最适合全局共享状态的方案,将月份、年份状态和切换方法封装到Context中,所有页面组件都可以直接获取:
步骤1:创建Context文件
// context/MonthContext.tsx import { createContext, useContext, useState, ReactNode } from 'react'; const months = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ]; interface MonthContextType { currentMonth: string; currentYear: number; monthNumber: number; changeMonth: (val: string) => void; } const MonthContext = createContext<MonthContextType | undefined>(undefined); export function MonthProvider({ children }: { children: ReactNode }) { const [currentMonth, setCurrentMonth] = useState<string>(months[new Date().getMonth()]); const [currentYear, setCurrentYear] = useState<number>(new Date().getFullYear()); const changeMonth = (val: string) => { if (val === "prev") { if (currentMonth === "January") { setCurrentMonth(months[11]); setCurrentYear(prev => prev - 1); } else { setCurrentMonth(months[months.indexOf(currentMonth) - 1]); } } else { if (currentMonth === "December") { setCurrentMonth(months[0]); setCurrentYear(prev => prev + 1); } else { setCurrentMonth(months[months.indexOf(currentMonth) + 1]); } } }; const monthNumber = months.indexOf(currentMonth); return ( <MonthContext.Provider value={{ currentMonth, currentYear, monthNumber, changeMonth }}> {children} </MonthContext.Provider> ); } export function useMonth() { const context = useContext(MonthContext); if (!context) { throw new Error('useMonth must be used within a MonthProvider'); } return context; }
步骤2:在_app.tsx中包裹Provider
// pages/_app.tsx import { MonthProvider } from '../context/MonthContext'; import type { AppProps } from 'next/app'; export default function App({ Component, pageProps }: AppProps) { return ( <MonthProvider> <Component {...pageProps} /> </MonthProvider> ); }
步骤3:在页面组件中使用状态
比如在首页pages/index.tsx:
// pages/index.tsx import { useMonth } from '../context/MonthContext'; import ViewSelector from '../components/ViewSelector'; import Summary from '../components/Summary'; export default function Home() { const { currentMonth, currentYear, monthNumber, changeMonth } = useMonth(); return ( <div className="App"> <ViewSelector /> <Summary monthNumber={monthNumber} month={currentMonth} year={currentYear} onClick={changeMonth} /> </div> ); }
2. URL查询参数传递状态
如果需要状态能通过URL分享或页面刷新后保留,可以将月份和年份作为URL查询参数传递:
在页面中使用useRouter管理参数
比如在首页:
// pages/index.tsx import { useState, useEffect } from 'react'; import { useRouter } from 'next/router'; import ViewSelector from '../components/ViewSelector'; import Summary from '../components/Summary'; const months = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ]; export default function Home() { const router = useRouter(); const [currentMonth, setCurrentMonth] = useState<string>( router.query.month as string || months[new Date().getMonth()] ); const [currentYear, setCurrentYear] = useState<number>( parseInt(router.query.year as string) || new Date().getFullYear() ); const changeMonth = (val: string) => { let newMonth = currentMonth; let newYear = currentYear; if (val === "prev") { if (currentMonth === "January") { newMonth = months[11]; newYear = currentYear - 1; } else { newMonth = months[months.indexOf(currentMonth) - 1]; } } else { if (currentMonth === "December") { newMonth = months[0]; newYear = currentYear + 1; } else { newMonth = months[months.indexOf(currentMonth) + 1]; } } // 更新URL查询参数 router.push({ pathname: router.pathname, query: { ...router.query, month: newMonth, year: newYear } }, undefined, { shallow: true }); }; // 监听URL参数变化更新状态 useEffect(() => { if (router.query.month) { setCurrentMonth(router.query.month as string); } if (router.query.year) { setCurrentYear(parseInt(router.query.year as string)); } }, [router.query]); const monthNumber = months.indexOf(currentMonth); return ( <div className="App"> <ViewSelector /> <Summary monthNumber={monthNumber} month={currentMonth} year={currentYear} onClick={changeMonth} /> </div> ); }
其他页面(如pages/calendar/[player].tsx)可以通过同样的方式读取router.query.month和router.query.year获取状态。
3. 客户端存储(LocalStorage/SessionStorage)
如果不需要URL同步,仅需要在客户端持久化状态,可以用LocalStorage存储:
在状态变化时存储到LocalStorage
// 以首页为例 import { useState } from 'react'; import ViewSelector from '../components/ViewSelector'; import Summary from '../components/Summary'; const months = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ]; export default function Home() { const [currentMonth, setCurrentMonth] = useState<string>( localStorage.getItem('currentMonth') || months[new Date().getMonth()] ); const [currentYear, setCurrentYear] = useState<number>( parseInt(localStorage.getItem('currentYear') || '') || new Date().getFullYear() ); const changeMonth = (val: string) => { let newMonth = currentMonth; let newYear = currentYear; if (val === "prev") { if (currentMonth === "January") { newMonth = months[11]; newYear = currentYear - 1; } else { newMonth = months[months.indexOf(currentMonth) - 1]; } } else { if (currentMonth === "December") { newMonth = months[0]; newYear = currentYear + 1; } else { newMonth = months[months.indexOf(currentMonth) + 1]; } } setCurrentMonth(newMonth); setCurrentYear(newYear); // 存储到LocalStorage localStorage.setItem('currentMonth', newMonth); localStorage.setItem('currentYear', newYear.toString()); }; const monthNumber = months.indexOf(currentMonth); return ( <div className="App"> <ViewSelector /> <Summary monthNumber={monthNumber} month={currentMonth} year={currentYear} onClick={changeMonth} /> </div> ); }
其他页面初始化时从LocalStorage读取即可。
方案选择
- React Context:适合全局共享、频繁更新的状态,无需URL同步,代码结构清晰。
- URL查询参数:适合需要分享页面状态、刷新页面保留状态的场景。
- LocalStorage:适合需要持久化状态但不需要URL同步的场景,注意存储容量限制和XSS风险。
内容的提问来源于stack exchange,提问作者moys
相关产品推荐
相关产品推荐

