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

如何在React类组件中获取URL参数并更新组件状态?解决Invalid hook call错误

在Next.js类组件中获取URL参数并更新数据的解决方案

嘿,我懂你碰到的问题了——你在类组件里尝试用React Hook(useRouter),但Hook只能在函数组件的顶层作用域里调用,所以才会抛出那个「Invalid hook call」的错误。别慌,在Next.js的类组件里获取路由参数有两种很实用的方法,我结合你的代码一步步给你讲清楚:

方法1:使用withRouter高阶组件

Next.js提供了withRouter高阶组件,可以把路由信息作为props注入到类组件中,这是类组件获取路由参数最直接的方式。

具体步骤:

  1. 从next/router导入withRouter
  2. 用withRouter包裹你的组件再导出
  3. 通过this.props.router访问路由参数(比如router.query.handle)
  4. 调整状态初始化和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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 06:55:59