如何在React类组件中获取URL参数并更新组件状态?解决Invalid hook call错误
在Next.js类组件中获取URL参数并更新数据的解决方案
嘿,我懂你碰到的问题了——你在类组件里尝试用React Hook(useRouter),但Hook只能在函数组件的顶层作用域里调用,所以才会抛出那个「Invalid hook call」的错误。别慌,在Next.js的类组件里获取路由参数有两种很实用的方法,我结合你的代码一步步给你讲清楚:
方法1:使用withRouter高阶组件
Next.js提供了withRouter高阶组件,可以把路由信息作为props注入到类组件中,这是类组件获取路由参数最直接的方式。
具体步骤:
- 从
next/router导入withRouter - 用
withRouter包裹你的组件再导出 - 通过
this.props.router访问路由参数(比如router.query.handle) - 调整状态初始化和API请求逻辑,使用路由参数替换硬编码的
bitcoin
修改后的代码示例:
import React from 'react'; import { withRouter } from 'next/router'; // 导入withRouter // 其他导入(Dashboard、Skeleton、Chip等)保持不变 class App extends React.Component { constructor(props) { super(props); // 初始化时从路由参数取handle,没有则默认'bitcoin' this.state = { coin: props.router.query.handle || 'bitcoin', CoinData: [], historicData: [], DataisLoaded: false, }; } componentDidMount() { this.fetchCoinData(this.state.coin); } // 封装API请求逻辑,避免重复代码 fetchCoinData(coin) { // 请求实时价格数据 fetch( `https://api.coingecko.com/api/v3/simple/price?ids=${coin}&vs_currencies=usd&include_market_cap=true&include_24hr_vol=true&include_24hr_change=true&include_last_updated_at=true` ) .then((res) => res.json()) .then((json) => { this.setState({ CoinData: json, DataisLoaded: true, }); }); // 请求历史数据 fetch( `https://api.coingecko.com/api/v3/coins/${coin}/market_chart?vs_currency=usd&days=3` ) .then((res) => res.json()) .then((json) => { this.setState({ historicData: json, }); }); } // 监听路由参数变化,用户导航到不同handle时重新请求数据 componentDidUpdate(prevProps) { if (prevProps.router.query.handle !== this.props.router.query.handle) { const newCoin = this.props.router.query.handle || 'bitcoin'; this.setState({ coin: newCoin, DataisLoaded: false }, () => { this.fetchCoinData(newCoin); }); } } render() { const { DataisLoaded, CoinData, historicData, coin } = this.state; const { router } = this.props; if (!DataisLoaded) return <Skeleton />; return ( <Dashboard> <DashboardHeader /> <DashboardSidebar /> <Main> <TopContainer> <div className='topContent'> <div role='presentation'> <Breadcrumbs aria-label='breadcrumb'> <Link underline='hover' href='/' fontSize='small' fontFamily='Titillium Web' color='#898181' > DefiPlatform </Link> <Link underline='hover' color='#898181' href='/coins/' fontSize='small' > Coins </Link> {/* 显示当前路由的handle */} <Typography color="#fff" fontSize="12px">{router.query.handle || 'bitcoin'}</Typography> </Breadcrumbs> </div> <div className='coinMain'> {/* 替换硬编码的bitcoin为state中的coin */} <h1> {coin} <Chip label={coin.toUpperCase()} variant='outlined' style={{ color: 'white', borderColor: '#251B55' }} /> </h1> <div> <Chip icon={<LinkIcon style={{ color: '#CECECE' }} />} size='small' label={`${coin}.org`} style={{ color: '#CECECE', marginTop: '5px', marginRight: '5px', backgroundColor: '#241E4E', }} /> <Chip icon={<ArticleOutlinedIcon style={{ color: '#CECECE' }} />} size='small' label='Whitepaper' style={{ color: '#CECECE', marginTop: '5px', marginRight: '5px', backgroundColor: '#241E4E', }} /> </div> </div> </div> <div className='coinPrice'> <div> <span className='coin_label'>{coin} Price ({coin.toUpperCase()})</span> <h2> {CoinData[coin].usd.toLocaleString('en-US', { maximumFractionDigits: 2, })}{' '} <Ticker Value='1.68%' Variant='BG' /> </h2> </div> <div className='coinComparison'> <div className='compareData'>12.47 ETH</div> <Ticker Value='2%' Variant='outline' /> </div> </div> </TopContainer> <MainCards> <Card> <h6>{coin} to USD Chart</h6> {coin} {JSON.stringify(CoinData[coin])} {JSON.stringify(historicData.prices)} </Card> <Card> <h6>{coin.toUpperCase()} Price Statistics</h6> </Card> </MainCards> </Main> </Dashboard> ); } } // 用withRouter包裹组件后导出 export default withRouter(App);
方法2:使用getServerSideProps(仅适用于页面组件)
如果你的组件是Next.js的页面组件(放在pages目录下,比如pages/coins/[handle].js),可以用getServerSideProps在服务端获取路由参数,然后作为props传给组件,这种方式适合需要服务端渲染(SSR)的场景。
修改后的代码示例:
import React from 'react'; // 其他导入保持不变 class App extends React.Component { constructor(props) { super(props); // 从props中获取服务端传来的coin参数 this.state = { coin: props.coin || 'bitcoin', CoinData: [], historicData: [], DataisLoaded: false, }; } componentDidMount() { this.fetchCoinData(this.state.coin); } fetchCoinData(coin) { // 同方法1的API请求逻辑,省略重复代码 } // 监听props变化,客户端导航时更新数据 componentDidUpdate(prevProps) { if (prevProps.coin !== this.props.coin) { const newCoin = this.props.coin || 'bitcoin'; this.setState({ coin: newCoin, DataisLoaded: false }, () => { this.fetchCoinData(newCoin); }); } } render() { // 同方法1的渲染逻辑,省略重复代码 } } // 服务端获取路由参数,传给组件props export async function getServerSideProps(context) { // context.params.handle对应路由文件[handle].js中的参数名 const { handle } = context.params; return { props: { coin: handle || 'bitcoin', }, }; } export default App;
关键注意事项
- 路由参数默认值:要考虑
handle参数为空的情况,给coin设置默认值(比如bitcoin),避免API请求出错。 - 客户端路由更新:如果用户通过Next.js的
Link组件导航到不同的handle,需要用componentDidUpdate监听参数变化,重新请求数据,否则组件不会自动刷新。 - API请求封装:把重复的API请求逻辑封装成单独的方法(比如
fetchCoinData),让代码更简洁易维护。
内容的提问来源于stack exchange,提问作者marion.serenio
相关产品推荐
相关产品推荐

