[英]React Router Link changes URL but doesn't render Component - Rest Countries API
I am building an app following the Rest Countries API challenge from frontendmentor ( https://www.frontendmentor.io/challenges/rest-countries-api-with-color-theme-switcher-5cacc469fec04111f7b848ca ). I am building an app following the Rest Countries API challenge from frontendmentor ( https://www.frontendmentor.io/challenges/rest-countries-api-with-color-theme-switcher-5cacc469fec04111f7b848ca ). I have run into a problem.我遇到了一个问题。 When clicking on the router link in countryDetail.js, the url changes but the component doesn't get re-rendered unless the page is refreshed.单击 countryDetail.js 中的路由器链接时,url 会发生变化,但除非刷新页面,否则组件不会重新呈现。
CountryDetails.js CountryDetails.js
import React, { useState, useEffect } from "react";
import axios from "axios";
import {Link} from "react-router-dom";
import {CountryDetailStyles, ImgContainer, CountryInfo, CountryInfoDetails, CountryDetailsWrapper, CountryInfoDetailsInner, CountryBorders, LinkWrapper} from "../styles/CountryDetailStyles";
function CountryDetail({match, history}) {
const [item, setItem] = useState([]);
const [allCountries, setAllCountries] = useState([]);
useEffect(() => {
fetchItem();
setBorderCountries();
}, []);
const fetchItem = async () => {
const response = await axios.get(`https://restcountries.eu/rest/v2/name/${match.params.name}?fullText=true`);
setItem(response.data)
}
const setBorderCountries = async () => {
const response = await axios.get(`https://restcountries.eu/rest/v2/all`);
setAllCountries(response.data)
}
// get borders full name using alpha3code
const getBorderCountryName = (allCountries, border) => {
const matchingCountry = allCountries.find(country => {
return country.alpha3Code === border;
})
return matchingCountry.name;
}
return (
<CountryDetailStyles>
<Link className="country-links" to={"/"}>
<LinkWrapper>
<p><i className="fas fa-arrow-left"></i>Go Back</p>
</LinkWrapper>
</Link>
{
item.length > 0 && item.map(i => (
<CountryDetailsWrapper>
<ImgContainer flag={i.flag}>
</ImgContainer>
<CountryInfo>
<h1>{i.name}</h1>
<CountryInfoDetails>
<CountryInfoDetailsInner>
<p><span>Native Name: </span>{i.nativeName}</p>
<p><span>Population: </span>{i.population.toLocaleString()}</p>
<p><span>Region: </span>{i.region.length > 0 ? i.region : 'N/A'}</p>
<p><span>Sub Region: </span>{i.subregion.length > 0 ? i.subregion : 'N/A'}</p>
<p><span>Capital: </span>{i.capital.length > 0 ? i.capital : 'N/A'}</p>
</CountryInfoDetailsInner>
<CountryInfoDetailsInner className="second">
<p><span>Top Level Domain: </span>{i.topLevelDomain}</p>
<p><span>Currencies: </span>{i.currencies[0].name}</p>
<p><span>Languages: </span>{i.languages.map(lang => lang.name).join(", ")}</p>
</CountryInfoDetailsInner>
</CountryInfoDetails>
<CountryBorders>
<p><span>Border Countries:</span></p>
{allCountries.length > 0 && i.borders.map(border => {
const borderName = getBorderCountryName(allCountries, border);
return (<Link to={`/country/${borderName.toLowerCase()}`}><button>{borderName}</button></Link>)
})}
</CountryBorders>
</CountryInfo>
</CountryDetailsWrapper>
))
}
</CountryDetailStyles>
)
}
export default CountryDetail;
App.js应用程序.js
import './App.css';
import { ThemeProvider } from 'styled-components';
import { lightTheme, darkTheme } from './styles/themes';
import { GlobalStyles } from './styles/global';
import { useState } from 'react';
import Navbar from './components/Navbar';
import { useDarkMode } from './hooks/useDarkMode';
import CountryList from './components/CountryList';
import CountryDetail from './components/CountryDetail';
import {Route} from "react-router-dom";
function App() {
const [theme, toggleTheme] = useDarkMode();
const themeMode = theme === 'dark' ? darkTheme : lightTheme;
return (
<div className="App">
<ThemeProvider theme={themeMode}>
<GlobalStyles />
<Navbar navTheme={theme} toggleTheme={toggleTheme} />
<Route exact path="/" component={CountryList} />
<Route exact path="/country/:name" component={CountryDetail} />
</ThemeProvider>
</div>
);
}
export default App;
Index.js索引.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom';
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
reportWebVitals();
The issue seems to be that you are already on the "/country/:name"
path and are clicking to visit another country.问题似乎是您已经在"/country/:name"
路径上,并且正在点击访问另一个国家。 The router correctly updates the URL in the address bar, but because CountryDetail
is already mounted it neglects to recompute the item
and allCountries
state.路由器正确更新地址栏中的 URL,但由于CountryDetail
已安装,它忽略重新计算item
和allCountries
。 This is because the useEffect
hook only runs once when the component mounts.这是因为useEffect
挂钩仅在组件挂载时运行一次。
The name
param ( match.params.name
) is actually a dependency for the GET requests, it should be added to the useEffect
hook's dependency array. name
参数( match.params.name
)实际上是 GET 请求的依赖项,它应该添加到useEffect
挂钩的依赖项数组中。
useEffect(() => {
fetchItem();
setBorderCountries();
}, [match.params.name]);
const fetchItem = async () => {
const response = await axios.get(
`https://restcountries.eu/rest/v2/name/${match.params.name}?fullText=true`
);
setItem(response.data);
}
It seems allCountries
doesn't have this dependency, so I recommend splitting it into its own useEffect
hook with empty dependency array so you fetch this data only once when the component mounts.似乎allCountries
没有这种依赖关系,所以我建议将其拆分为自己的useEffect
钩子和空依赖数组,以便在组件挂载时仅获取此数据一次。
useEffect(() => {
setBorderCountries();
}, []);
useEffect(() => {
fetchItem();
}, [match.params.name]);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.