React 路由器 Link 更改 URL,但不渲染组件
React router Link changes URL, but doesn't render the component
我在我的 React 应用程序中有一个使用哈希路由器的简单路由系统。我有一个链接到路线 /assets/id
的 MainDisplay
。我还有一个名为 Trending
的组件,它链接到相同的路由 /assets/id
。 MainDisplay
中的 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 (....);
}
要解决,请将 api
和 fetchApi
函数移动到 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 (...);
}
我在我的 React 应用程序中有一个使用哈希路由器的简单路由系统。我有一个链接到路线 /assets/id
的 MainDisplay
。我还有一个名为 Trending
的组件,它链接到相同的路由 /assets/id
。 MainDisplay
中的 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 (....);
}
要解决,请将 api
和 fetchApi
函数移动到 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 (...);
}