如何在Next.js中使用React Context共享动态状态
问题原因
你猜的没错:React Context 结合 useState 存储的状态是内存级的,当页面发生服务器端导航(比如直接输入URL、跳转触发服务器渲染)或页面刷新时,整个React应用会重新初始化,内存中的状态会被清空,回到useState的初始值{}。哪怕Provider挂载在_app.js根组件上,只要组件实例重建,状态就会重置。
解决方案
方案1:通过URL传递交易ID,详情页单独获取数据(最推荐)
这是跨页面传递数据的标准实践,不依赖客户端内存状态,支持页面刷新、SEO,可靠性最高。
- 表格页面跳转时携带ID:
import { useRouter } from 'next/router' const TransactionTable = () => { const router = useRouter() const handleRowClick = (transaction) => { // 将交易ID放入URL参数,跳转到详情页 router.push(`/transactions/${transaction.id}`) } return ( <table> {/* 表格内容,每行绑定点击事件 */} <tbody> {transactions.map(transaction => ( <tr key={transaction.id} onClick={() => handleRowClick(transaction)}> <td>{transaction.id}</td> <td>{transaction.amount}</td> </tr> ))} </tbody> </table> ) }
- 详情页通过URLID获取数据:
创建src/pages/transactions/[id].js页面:
import { useRouter } from 'next/router' import { useEffect, useState } from 'react' const TransactionDetail = () => { const router = useRouter() const { id } = router.query const [transaction, setTransaction] = useState(null) useEffect(() => { if (id) { // 调用API获取对应ID的交易详情 fetch(`/api/transactions/${id}`) .then(res => res.json()) .then(data => setTransaction(data)) } }, [id]) if (!transaction) return <div>加载中...</div> return ( <div> <h1>交易详情:{transaction.id}</h1> <p>金额:{transaction.amount}</p> {/* 其他交易信息 */} </div> ) } export default TransactionDetail
方案2:持久化状态到LocalStorage,保留Context数据
如果必须通过Context共享状态,可以将数据持久化到本地存储,页面刷新后自动恢复:
修改src/context/TransactionContext.js:
import { useContext, createContext, useState, useEffect } from 'react' const TransactionContext = createContext() export const TransactionProvider = ({ children }) => { // 初始化时从LocalStorage读取数据(判断window避免SSR报错) const [transactionData, setTransactionData] = useState(() => { if (typeof window !== 'undefined') { const savedData = localStorage.getItem('transactionData') return savedData ? JSON.parse(savedData) : {} } return {} }) // 状态变化时同步到LocalStorage useEffect(() => { localStorage.setItem('transactionData', JSON.stringify(transactionData)) }, [transactionData]) return ( <TransactionContext.Provider value={{ transactionData, setTransactionData }} > {children} </TransactionContext.Provider> ) } export const useTransactionContext = () => { const context = useContext(TransactionContext) if (!context) throw new Error('Transaction context is not defined here!!') return context }
注意:此方案仅适合客户端场景,存储数据不宜过大,且需考虑数据过期问题(比如用户长时间不操作,存储数据可能已失效)。
方案3:使用客户端导航保留内存状态(临时场景)
如果你的页面是纯客户端渲染,且跳转用Next.js的客户端导航(不触发服务器请求),Provider的内存状态会保留:
不要用window.location.href跳转,改用以下方式:
// 方式1:用next/link import Link from 'next/link' import { useTransactionContext } from 'context/TransactionContext' const TransactionTable = () => { const { setTransactionData } = useTransactionContext() return ( <table> <tbody> {transactions.map(transaction => ( <tr key={transaction.id}> <Link href="/transaction-detail" passHref> <td onClick={() => setTransactionData(transaction)} style={{ cursor: 'pointer' }} > {transaction.id} </td> </Link> </tr> ))} </tbody> </table> ) }
// 方式2:用useRouter import { useRouter } from 'next/router' import { useTransactionContext } from 'context/TransactionContext' const TransactionTable = () => { const router = useRouter() const { setTransactionData } = useTransactionContext() const handleRowClick = (transaction) => { setTransactionData(transaction) router.push('/transaction-detail') } // ... 渲染表格 }
此方案局限性大:用户刷新详情页后状态会丢失,仅适合临时跳转的非关键数据场景。
内容的提问来源于stack exchange,提问作者Genaro Bonavita
相关产品推荐
相关产品推荐

