React 路由器 Link 更改 URL,但不渲染组件

React router Link changes URL, but doesn't render the component

我在我的 React 应用程序中有一个使用哈希路由器的简单路由系统。我有一个链接到路线 /assets/idMainDisplay。我还有一个名为 Trending 的组件,它链接到相同的路由 /assets/idMainDisplay 中的 link 完美运行,但 Trending 中的 link 却不行。 Trending 的 link 在我单击它时更改了 URL,但在我刷新之前不会将我发送到正确的页面。

趋势组件由另一个名为 CryptoInfo 的组件呈现,该组件包含基于我从 MainDisplay 获得的 id 的信息。谁能帮我理解为什么我的路由在 Trending 组件中不起作用?

// MainDisplay.js

      <Link key={index} to={`/assets/${id}`}>
          <span>{name}</span>
          <span>${current_price}</span>
      </Link>
// CryptoInfo

import React, { useState, useEffect } from 'react'
import { useParams } from "react-router-dom"
import '../../styles/CryptoInfo.css'
import MainNav from '../nav/MainNav'
import Chart from './Chart'
import Description from './Description'
import Footer from '../main/Footer'
import Exchanges from './Exchanges'
import News from './News'
import Trending from './Trending'

export default function CryptoInfo() {
    const [currentData, setCurrentData] = useState([])
    const [image, setImage] = useState("")
    const [currentPrice, setCurrentPrice] = useState("")
    const [marketCap, setMarketCap] = useState("")
    const [marketData, setMarketData] = useState([])
    const [days, setDays] = useState(1)
    const [usd, setUsd] = useState('')
    const [currency, setCurrency] = useState('')


    const { id } = useParams()
    const api = `https://api.coingecko.com/api/v3/coins/${id}`


    async function fetchApi() {
        const data = await fetch(api)
        const response = await data.json()
        setCurrentData(response)
        setImage(response.image.small)
        setCurrentPrice(response.market_data.current_price.usd)
        setMarketData(response.market_data)
        setMarketCap(response.market_data.market_cap.usd)
    }

    useEffect(() => {
        fetchApi()
    }, [days])


    const currentDataDisplay = (
        <>
            <div className='top-div'>
                <div >
                    <span className='market-rank'>Rank: {market_cap_rank}</span>
                </div>
                <div className='name-div'>
                    <img className='coin-image' src={image} alt="" />
                    <span className='crypto-name'>{name}</span>
                    <span className="c-info-symbol">{SYMBOL}</span>
                </div>
            </div>
            {/* <div className='todays-range'>
                <div>
                    <progress className='progress-bar' min="0" max="0.14" value="0.1"></progress>
                </div>
                <div>
                    <span>{todayLow}</span>
                    <span>
                        -
                    </span>
                    <span> {todayHigh}</span>
                </div>
            </div> */}
            <div className='price-div'>
                <span className='current-price'>${current_price}</span>
                <span className='price-change-24' style={{ color: priceChange24h > 0 ? 'green' : 'red' }} >  {priceChange24h}  %</span>
            </div>
        </>
    )





    return (
        <>
            <MainNav />
            <div className="crypto-info-container">
                <div className="crypto-info-top-container">
                    <div>
                        {currentDataDisplay}
                     </div>
                    <Chart />
                </div>

                <div className='crypto-info-bottom-container'>
                    <div className='des-container'>
                        <Description symbol={SYMBOL} />
                        <Exchanges />
                    </div>
                    <News />
                </div>
                <Trending />
            </div>
            <Footer />
        </>
    )
}

          
// Trending.js

import React, { useEffect, useState } from 'react'
import { Paper } from '@mui/material'
import { Link } from 'react-router-dom'

export default function Trending() {
    const [trendingData, setTrendingData] = useState([])
    const [trendingStats, setTrendingStats] = useState([])

    const api = 'https://api.coingecko.com/api/v3/search/trending'

    useEffect(() => {
        fetchData()
    }, [])

    const fetchData = async () => {
        const data = await fetch(api)
        const response = await data.json()
        setTrendingData(response.coins)
    }

    const fetchMarketData = async () => {
        const data = await fetch(fetchAll)
        const response = await data.json()
        setTrendingStats(response)
    }


    let coinsArray = []
    trendingData && trendingData.forEach((item) => {
        const { id } = item.item
        coinsArray.push(id + '%2C%20')
    })

    const allIds = coinsArray.join('').replace(',', '').slice(0, -6)

    const fetchAll = `https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&ids=${allIds}&order=market_cap_desc&per_page=100&page=1&sparkline=true`



    useEffect(() => {
        fetchMarketData()
    }, [allIds, trendingStats])


    const trendingDivv1 = (
        trendingStats && trendingStats.slice(0, 4).map((coin, index) => {
            const { name, price_change_percentage_24h, image, id, current_price } = coin
            const sparkline = coin.sparkline_in_7d.price
            let borderColor
            let changePercent = price_change_percentage_24h.toFixed(2)

       
            return (
                <Link key={index} to={`/assets/${id}`}>
                    <Paper className="trending-box" elevation={3}>
                        <div style={{ display: 'flex' }}>
                            <div><img style={{ borderRadius: '50%', width: '50px' }} src={image} alt="" /></div>
                            <div style={{ display: 'flex', flexDirection: 'column' }}>
                                <span>{name}</span>
                                <span>${current_price}</span>
                            </div>
                            <span style={{ color: borderColor, marginLeft: 'auto' }}>{changePercent}%</span>
                        </div>
                        <div className='trending-chart'>
                            <Sparklines data={sparkline}>
                                <SparklinesLine color={borderColor} />
                            </Sparklines>
                        </div>
                    </Paper >
                </Link>
            )
        })
    

    const trendingDivv2 = (
        trendingStats && trendingStats.slice(4, 8).map((coin, index) => {
            const { name, price_change_percentage_24h, image, id, current_price } = coin
            const sparkline = coin.sparkline_in_7d.price
            let borderColor
            let changePercent = price_change_percentage_24h.toFixed(2)

      

            return (
                 <Link to={`/assets/${id}`}>
                    <Paper className="trending-box" elevation={3}>
                        <div style={{ display: 'flex' }}>
                            <div><img style={{ borderRadius: '50%', width: '50px', height: '50px' }} src={image} alt="" /></div>
                            <div style={{ display: 'flex', flexDirection: 'column' }}>
                                <span> {name}</span>
                                <span>${current_price}</span>
                            </div>
                            <span style={{ color: borderColor, marginLeft: 'auto' }}>{changePercent}%</span>
                        </div>
                        <div className='trending-chart'>
                            <Sparklines data={sparkline}>
                                <SparklinesLine color={borderColor} />
                            </Sparklines>
                        </div>
                    </Paper >
                </Link >

            )
        })
    )



    return (
        <>
           

                    <div className='trending-div'>
                        <h1 style={{ margin: '20px 0' }}>Trending Coins </h1>
                        <div className='trending-coins-div'>
                            <div className='trending-flex-box'>
                                {trendingDivv1}
                            </div>
                            <div className='trending-flex-box'>
                                {trendingDivv2}
                                <a href={'/'}>
                                    <div className="trending-box trending-image" >
                                        <h2 style={{ color: 'white', fontWeight: '700', fontSize: '1.5em', verticalAlign: 'center' }}>See More Coins</h2>
                                    </div >
                                </a>
                            </div>
                        </div>
                    </div>
            
        </>
    )
}




//App.js

     <>
      <Router>
        <Switch>

          <Route exact path="/" >
            <div>
              <MainNav />
              <InfoNav />
              <MainDisplay />
              <Footer />
            </div>
          </Route>


          <Route path="/assets/:id">
            <CryptoInfo />
          </Route>

          <Route path="/categories">
            <CategoryList />
          </Route>

        </Switch>
      </Router>
    </>

所以 Trending 中的 link 是 工作。问题是 CryptoInfo 组件不响应 id 路由参数更新以获取任何新数据。

export default function CryptoInfo() {
  ...
  const [days, setDays] = useState(1); // <-- (3) setDays never called, days never updates
  ...

  const { id } = useParams(); // <-- (1) id param updates
  const api = `https://api.coingecko.com/api/v3/coins/${id}`; // <-- (2) api value updated

  async function fetchApi() {
    ...
  }

  useEffect(() => {
    fetchApi(); // <-- (5) no refetch
  }, [days]); // <-- (4) dependency doesn't update

  ...

  return (....);
}

要解决,请将 apifetchApi 函数移动到 useEffect 中并使用正确的依赖项,在本例中只是 id。您还应该在 try/catch 中包含任何异步代码,以处理任何被拒绝的承诺或任何其他抛出的异常。

示例:

function CryptoInfo() {
  ...

  const { id } = useParams();

  useEffect(() => {
    const api = `https://api.coingecko.com/api/v3/coins/${id}`;

    async function fetchApi() {
      try {
        const data = await fetch(api);
        const response = await data.json();
        setCurrentData(response);
        setImage(response.image.small);
        setCurrentPrice(response.market_data.current_price.usd);
        setMarketData(response.market_data);
        setMarketCap(response.market_data.market_cap.usd);
      } catch (error) {
        // handle error
        console.log(error);
      }
    }

    fetchApi();
  }, [id]);

  ...

  return (...);
}