首頁  >  文章  >  web前端  >  使用 React 建立國家/地區查找應用程式

使用 React 建立國家/地區查找應用程式

DDD
DDD原創
2024-09-13 14:15:261006瀏覽

Building a Country Finder Application with React

介紹

在這篇文章中,我們將探索如何使用 React 建立國家/地區尋找應用程式。該應用程式允許用戶搜尋國家/地區、按地區過濾它們以及查看有關每個國家/地區的詳細資訊。我們將利用 React 的鉤子和上下文來管理狀態和主題,並將與 REST 國家/地區 API 整合以獲取國家/地區資料。

項目概況

國家/地區查找應用程式提供了一個互動式介面,使用者可以:

  • 按名稱搜尋國家。
  • 依地區篩選國家。
  • 查看每個國家/地區的詳細信息,包括國旗、人口等。

特徵

  • 搜尋欄:允許使用者以名稱搜尋國家。
  • 依地區過濾:下拉式選單可依地區過濾國家。
  • 國家/地區詳細資訊:顯示所選國家/地區的詳細資訊。
  • 主題切換:在淺色和深色主題之間切換。

使用的技術

  • React:用於建立使用者介面的 JavaScript 函式庫。
  • REST 國家/地區 API:提供有關國家/地區的資料。
  • CSS:用於設計應用程式的樣式。
  • React Router:用於在頁面之間導航和傳遞狀態。

專案結構

專案分為幾個組件:

  • App.js:主要元件,包括用於路由的 Header 和 Outlet。
  • Header.js:顯示應用程式標題和主題切換按鈕。
  • Home.js:帶有搜尋和過濾選項的主頁,以及國家/地區列表。
  • SearchBar.js:搜尋國家/地區的元件。
  • SelectMenu.js:用於按地區過濾國家/地區的下拉式選單。
  • CountriesList.js:根據搜尋和篩選條件顯示國家列表。
  • CountryCard.js:顯示每個國家的摘要。
  • CountryDetail.js:顯示所選國家的詳細資訊。
  • CountryDetailShimmer.js:載入國家/地區詳細資料的佔位符。
  • Error.js:路由的錯誤處理元件。

安裝

  1. 克隆儲存庫
   git clone https://github.com/abhishekgurjar-in/country-finder.git
   cd country-finder
  1. 安裝依賴項
   npm install
  1. 啟動開發伺服器
   npm start

用法

  1. 搜尋國家:在搜尋欄中輸入國家/地區名稱以過濾國家列表。
  2. 按地區過濾:從下拉式選單中選擇一個地區以查看該地區的國家。
  3. 查看詳細資料:點擊國家卡即可查看該國家/地區的詳細資訊。

程式碼說明

應用程式.js

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;

header.js

標題元件可讓使用者在淺色和深色主題之間切換並顯示應用程式標題。

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>
  )
}

首頁.js

主頁元件包含搜尋列、篩選選單,並根據搜尋和篩選條件列出國家。

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>
  )
}

搜尋欄.js

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>
  )
}

選擇選單.js

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>
  )
}

國家列表.js

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.js

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.js

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.js

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>
  );
}

國家列表Shimmer.js

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 的鉤子和上下文來管理狀態和主題。

製作人員

  • 反應:反應
  • REST 國家 API:REST 國家/地區
  • 很棒的字體:很棒的字體

作者

Abhishek Gurjar 是一位專注的 Web 開發人員,熱衷於創建實用且功能性的 Web 應用程式。在 GitHub 上查看他的更多專案。

以上是使用 React 建立國家/地區查找應用程式的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn