在这篇博文中,我们将探索如何使用 React 构建国家/地区查找应用程序。该应用程序允许用户搜索国家/地区、按地区过滤它们以及查看有关每个国家/地区的详细信息。我们将利用 React 的钩子和上下文来管理状态和主题,并将与 REST 国家/地区 API 集成以获取国家/地区数据。
国家/地区查找应用程序提供了一个交互式界面,用户可以:
该项目分为几个组件:
git clone https://github.com/abhishekgurjar-in/country-finder.git cd country-finder
npm install
npm start
App 组件将 Header 和 Outlet 组件包装在 ThemeProvider 中,管理整个应用程序的主题状态。
import Header from "./components/Header"; import { Outlet } from "react-router-dom"; import "./App.css"; import { ThemeProvider } from "./contexts/ThemeContext"; const App = () => { return ( <ThemeProvider> <Header /> <Outlet /> </ThemeProvider> ); }; export default App;
标题组件允许用户在浅色和深色主题之间切换并显示应用程序标题。
import { useTheme } from "../hooks/useTheme" export default function Header() { const [isDark, setIsDark] = useTheme(); return ( <header className={`header-container ${isDark ? 'dark' : ''}`}> <div className="header-content"> <h2 className="title"> <a href="/">Country Finder</a> </h2> <p className="theme-changer" onClick={() => { setIsDark(!isDark); localStorage.setItem('isDarkMode', !isDark); }}> <i className={`fa-solid fa-${isDark ? 'sun' : 'moon'}`} /> {isDark ? 'Light' : 'Dark'} Mode </p> </div> </header> ) }
主页组件包含搜索栏、筛选菜单,并根据搜索和筛选条件列出国家/地区。
import React, { useState } from 'react'; import SearchBar from './SearchBar'; import SelectMenu from './SelectMenu'; import CountriesList from './CountriesList'; import { useTheme } from '../hooks/useTheme'; export default function Home() { const [query, setQuery] = useState(''); const [isDark] = useTheme(); return ( <main className={`${isDark ? 'dark' : ''}`}> <div className="search-filter-container"> <SearchBar setQuery={setQuery} /> <SelectMenu setQuery={setQuery} /> </div> <CountriesList query={query} /> </main> ) }
SearchBar 组件处理搜索国家/地区的用户输入。
import React from 'react'; export default function SearchBar({ setQuery }) { return ( <div className="search-container"> <i className="fa-solid fa-magnifying-glass"></i> <input onChange={(e) => setQuery(e.target.value.toLowerCase())} type="text" placeholder="Search for a country..." /> </div> ) }
SelectMenu 组件提供了一个下拉菜单,用于按地区过滤国家/地区。
import React from 'react'; export default function SelectMenu({ setQuery }) { return ( <select className="filter-by-region" onChange={(e) => setQuery(e.target.value.toLowerCase())}> <option hidden>Filter by Region</option> <option value="Africa">Africa</option> <option value="Americas">Americas</option> <option value="Asia">Asia</option> <option value="Europe">Europe</option> <option value="Oceania">Oceania</option> </select> ) }
CountriesList 组件获取并显示国家/地区列表。
import React, { useEffect, useState } from 'react'; import CountryCard from './CountryCard'; import CountriesListShimmer from './CountriesListShimmer'; export default function CountriesList({ query }) { const [countriesData, setCountriesData] = useState([]); useEffect(() => { fetch('https://restcountries.com/v3.1/all') .then((res) => res.json()) .then((data) => { setCountriesData(data); }); }, []); if (!countriesData.length) { return <CountriesListShimmer />; } return ( <div className="countries-container"> {countriesData .filter((country) => country.name.common.toLowerCase().includes(query) || country.region.toLowerCase().includes(query) ) .map((country) => ( <CountryCard key={country.name.common} name={country.name.common} flag={country.flags.svg} population={country.population} region={country.region} capital={country.capital?.[0]} data={country} /> ))} </div> ) }
CountryDetail 组件获取并显示有关所选国家/地区的详细信息。
import React, { useEffect, useState } from 'react'; import { Link, useLocation, useParams } from 'react-router-dom'; import { useTheme } from '../hooks/useTheme'; import CountryDetailShimmer from './CountryDetailShimmer'; import './CountryDetail.css'; export default function CountryDetail() { const [isDark] = useTheme(); const params = useParams(); const { state } = useLocation(); const countryName = params.country; const [countryData, setCountryData] = useState(null); const [notFound, setNotFound] = useState(false); function updateCountryData(data) { setCountryData({ name: data.name.common || data.name, nativeName: Object.values(data.name.nativeName || {})[0]?.common, population: data.population, region: data.region, subregion: data.subregion, capital: data.capital, flag: data.flags.svg, tld: data.tld, languages: Object.values(data.languages || {}).join(', '), currencies: Object.values(data.currencies || {}) .map((currency) => currency.name) .join(', '), borders: [], }); if (!data.borders) { data.borders = []; } Promise.all( data.borders.map((border) => fetch(`https://restcountries.com/v3.1/alpha/${border}`) .then((res) => res.json()) .then(([borderCountry]) => borderCountry.name.common) ) ).then((borders) => { setTimeout(() => setCountryData((prevState) => ({ ...prevState, borders })) ); }); } useEffect(() => { if (state) { updateCountryData(state); return; } fetch(`https://restcountries.com/v3.1/name/${countryName}?fullText=true`) .then((res) => res.json()) .then(([data]) => { if (!data) { setNotFound(true); } else { updateCountryData(data); } }) .catch(() => setNotFound(true)); }, [countryName, state]); if (notFound) { return ( <div className={`error-container ${isDark ? 'dark' : ''}`}> <h3>Country not found</h3> <Link to="/">Back to home</Link> </div> ); } if (!countryData) { return <CountryDetailShimmer />; } return ( <div className={`country-detail-container ${isDark ? 'dark' : ''}`}> <Link to="/" className="back-button"> <i className="fa-solid fa-arrow-left" /> Back </Link> <div className="country-detail-content"> <img src={countryData.flag} alt={`${countryData.name} flag`} /> <div className="country-detail-info"> <h1>{countryData.name}</h1> <div className="details"> <p><strong>Native Name:</strong> {countryData.nativeName}</p> <p><strong>Population:</strong> {countryData.population}</p> <p><strong>Region:</strong> {countryData.region}</p> <p><strong>Subregion:</strong> {countryData.subregion}</p> <p><strong>Capital:</strong> {countryData.capital}</p> <p><strong>Top Level Domain:</strong> {countryData.tld}</p> <p><strong>Languages:</strong> {countryData.languages}</p> <p><strong>Currencies:</strong> {countryData.currencies}</p> <p><strong>Border Countries:</strong> {countryData.borders.join(', ') || 'None'}</p> </div> </div> </div> </div> ); }
CountryDetailShimmer 组件在获取国家/地区详细信息时显示加载占位符。
import React from 'react'; export default function CountryDetailShimmer() { return ( <div className="country-detail-shimmer"> <div className="shimmer-img"></div> <div className="shimmer-info"> <div className="shimmer-line name"></div> <div className="shimmer-line"></div> <div className="shimmer-line"></div> <div className="shimmer-line"></div> <div className="shimmer-line"></div> </div> </div> ); }
CountryCard 组件显示每个国家/地区的简要概述。
import React from 'react'; import { Link } from 'react-router-dom'; export default function CountryCard({ name, flag, population, region, capital, data }) { return ( <div className="country-card"> <img src={flag} alt={`${name} flag`} /> <h3>{name}</h3> <p><strong>Population:</strong> {population}</p> <p><strong>Region:</strong> {region}</p> <p><strong>Capital:</strong> {capital}</p> <Link to={`/country/${name}`} state={data}> <button>More Details</button> </Link> </div> ); }
CountriesListShimmer 组件在获取国家/地区列表时显示加载占位符。
import React from 'react'; export default function CountriesListShimmer() { return ( <div className="countries-list-shimmer"> {Array.from({ length: 10 }).map((_, index) => ( <div key={index} className="shimmer-card"></div> ))} </div> ); }
您可以通过访问国家/地区查找器演示来观看国家/地区查找器应用程序的实时演示。
在这个项目中,我们使用 React 构建了一个国家/地区查找应用程序,允许用户搜索国家/地区、按地区过滤并查看详细信息。我们与 REST Country API 集成,并使用 React 的钩子和上下文来管理状态和主题。
Abhishek Gurjar 是一位专注的 Web 开发人员,热衷于创建实用且功能性的 Web 应用程序。在 GitHub 上查看他的更多项目。
以上是使用 React 构建国家/地区查找应用程序的详细内容。更多信息请关注PHP中文网其他相关文章!