You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 21:54:58