- Введение
- Необходимые условия
- Демонстрация и ссылки на Github
- Установка
- Удаление ненужного CSS
- Пакеты
- React-Router:
- ChakraIcons:
- Компонент Navlink
- Маршрутизация:
- Главная страница:
- Импортируем все необходимые компоненты
- Состояния:
- Api:
- Выбор региона и функции поиска страны
- Импортная панель навигации
- Рендеринг данных
- Компонент одной страны
- App.tsx
Введение
В этой статье я расскажу, как я решил задачу Frontend mentor «REST Countries API с переключателем цветовой темы» с помощью Chakra UI и Create React App. В конце этого руководства мы должны быть в состоянии
-
Видеть все страны из API на домашней странице
-
Искать страну с помощью поля ввода
-
Фильтровать страны по регионам
-
Нажимать на страну, чтобы увидеть более подробную информацию на
отдельная страница -
Переход к пограничным странам на странице подробной информации
-
Переключение цветовой схемы между светлым и темным режимом
(необязательно)
Необходимые условия
Чтобы следовать этому руководству, вы должны иметь базовое представление о следующем.
-
Базовые знания синтаксиса и возможностей JavaScript ES6
-
Основы терминологии ReactJS: JSX, State, Asynchronous
JavaScript и т.д. -
Базовое понимание Restful API.
-
Базовые знания TypeScript
-
Базовое понимание Chakra UI
-
Базовые знания React Router
Демонстрация и ссылки на Github
Решение на Github
Живой сайт
Разбивка по компонентам
- Компонент заголовка
- Главный компонент
- Одностраничный компонент
Установка
Перед сборкой каждого компонента мы начнем с создания нового проекта create-react-app из шаблона с использованием автоматического шаблона typescript Chakra UI, как показано в коде ниже.
# TypeScript using npm
npx create-react-app my-app --template @chakra-ui/typescript
Эта команда загрузит готовое к использованию приложение react.
После создания нашего приложения структура папок должна выглядеть так, как показано на рисунке ниже.
Теперь мы переходим в папку my-app и запускаем наше приложение, выполнив команду npm start
. У нас должно получиться что-то похожее на изображение ниже.
Удаление ненужного CSS
Теперь, когда мы создали проект create-react-app с помощью шаблона, нам осталось только начать создавать наши компоненты и очистить файлы.
В папке src я создал две новые папки: pages
и components
.
Пакеты
В дополнение к нашим предустановленным пакетам из шаблонов create-react-app typescript, мы установим еще два дополнительных пакета, а именно
React-Router:
Он поможет нам с маршрутизацией внутри нашего приложения.
ChakraIcons:
Предоставит набор часто используемых иконок интерфейса, которые можно использовать в нашем проекте.
Теперь мы можем перейти в нашу корневую папку и запустить приведенный ниже код, чтобы установить оба пакета вместе.
npm install react-router-dom@6 @chakra-ui/icons
Компонент Navlink
Здесь мы используем шаблон Chakra UI Navbar с пользовательским выпадающим меню и переключателем тем Dark.
import {
Box,
Flex,
Button,
useColorModeValue,
Stack,
useColorMode,
} from '@chakra-ui/react';
import { MoonIcon, SunIcon } from '@chakra-ui/icons';
import { useNavigate } from 'react-router-dom';
export default function Nav() {
const { colorMode, toggleColorMode } = useColorMode();
let navigate = useNavigate();
return (
<>
<Box bg={useColorModeValue('gray.100', 'gray.900')} px={4}>
<Flex h={16} alignItems={'center'} justifyContent={'space-between'}>
<Box onClick={()=> navigate('/')} >Where in the world?</Box>
<Flex alignItems={'center'}>
<Stack direction={'row'} spacing={7}>
<Button onClick={toggleColorMode}>
{colorMode === 'light' ? <MoonIcon /> : <SunIcon />}
</Button>
</Stack>
</Flex>
</Flex>
</Box>
</>
);
}
Для этого компонента мы используем ReactRouter для простой маршрутизации и chakra-UI/icons для доступа к иконкам Chakra UI.
Маршрутизация:
Здесь мы связываем наши компоненты с соответствующими страницами.
import React from 'react'
import {Route, Routes } from "react-router-dom";
import Home from '../Pages/Home';
import SingleCountry from '../Pages/SingleCountry';
function Routing() {
return (
<div>
<Routes>
<Route path="/" element={<Home/>} />
<Route path="/singlecountry/:countryname" element={<SingleCountry/>} />
</Routes>
</div>
)
}
export default Routing
Теперь, когда у нас есть наша панель навигации и компоненты маршрутизации, мы переходим к созданию файла домашней страницы
Главная страница:
// Importing
import React from "react";
import { useState, useEffect } from "react";
import {
Flex,
GridItem,
Image,
Input,
InputGroup,
InputLeftElement,
Select,
SimpleGrid,
Spacer,
} from "@chakra-ui/react";
import { Box } from "@chakra-ui/react";
import { useNavigate } from "react-router-dom";
import { Progress } from "@chakra-ui/react";
import { SearchIcon } from "@chakra-ui/icons";
import Nav from "../Components/Navlink";
function Home() {
//States
const [data, setData] = useState([]);
const [data2, setData2] = useState([]);
const [searchInput, setSearchInput] = useState("");
const [selectInput, setSelectInput] = useState("all");
let navigate = useNavigate();
//Calling Apis
useEffect(() => {
if (selectInput === "all") {
fetch(`https://restcountries.com/v3.1/all`)
.then((res) => res.json())
.then((data) => {
return (
setData(data),
setData2(data))
})
.catch((err) => console.log("Error:", err.message));
} else {
fetch(`https://restcountries.com/v3.1/region/${selectInput}`)
.then((res) => res.json()).then((data)=>{
return (
setData(data),
setData2(data)
)
})
.catch((err) => console.log("Error:", err.message));
}
}, [selectInput]);
//Handle Region select
const handleChangeSelect = (e) => {
setSelectInput(e.target.value);
};
//Handle Country Search
const handleChangeInput = (e) => {
e.preventDefault();
setSearchInput(e.target.value);
setData(
data2.filter((x) =>
x?.name?.common
?.toLowerCase()
?.includes(e?.target?.value?.toLowerCase())
)
);
};
return (
<div>
{/* Navbar */}
<Nav/>
{/*
Country Search and Region Select form */}
<form>
<Flex pr="50" pl="50" flexWrap={"wrap"}>
<Box p="4">
<InputGroup>
<InputLeftElement
pointerEvents="none"
children={<SearchIcon color="gray.300" />}
/>
<Input
value={searchInput}
onChange={handleChangeInput}
type="text"
placeholder="Search for a country "
/>
</InputGroup>
</Box>
<Spacer />
<Box p="4">
<Select onChange={handleChangeSelect} placeholder="Select option">
<option value="all">All</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>
</Box>
</Flex>
</form>
{/* Data Rendering */}
{data2?.length === 0 ? (
<Progress colorScheme="pink" size="xs" isIndeterminate />
) : (
<Box w="100%">
<SimpleGrid
columns={[1, null, 4]}
spacing={10}
pt="100"
pr="50"
pl="50"
>
{data?.map((x) => (
<GridItem
key={x?.name?.common}
onClick={() =>
navigate(`/singlecountry/${x?.cca2?.toLowerCase()}`, {})
}
>
<Box
maxW="sm"
borderWidth="1px"
borderRadius="lg"
overflow="hidden"
>
<Image
src={x?.flags?.svg}
alt={x?.name?.common}
height="200px"
width="100%"
/>
<Box p="6">
<Box
mt="1"
fontWeight="semibold"
as="h4"
lineHeight="tight"
noOfLines={1}
>
{x?.name?.common}
</Box>
<Box
mt="1"
fontWeight="semibold"
as="h4"
lineHeight="tight"
noOfLines={1}
>
Population: {x?.population}
</Box>
<Box
mt="1"
fontWeight="semibold"
as="h4"
lineHeight="tight"
noOfLines={1}
>
Region: {x?.region}
</Box>
<Box
mt="1"
fontWeight="semibold"
as="h4"
lineHeight="tight"
noOfLines={1}
>
Capital: {x?.capital}
</Box>
</Box>
</Box>
</GridItem>
))}
</SimpleGrid>
</Box>
)}
</div>
);
}
export default Home;
Как вы можете видеть, здесь много строк кода, которые нужно осмыслить сразу, поэтому давайте делать по шагу за раз.
Импортируем все необходимые компоненты
Здесь мы импортируем Flex, grid, item, Image, Input, InputGroup, InputLeftElement, Select, SimpleGrid, Spacer, Box, Progress из Chakra UI, используем navigate из react-router, SearchIcon из ChakraIcons и Nav из нашего компонента Navlinks.
// Importing
import React from "react";
import { useState, useEffect } from "react";
import {
Flex,
GridItem,
Image,
Input,
InputGroup,
InputLeftElement,
Select,
SimpleGrid,
Spacer,
Box,
Progress
} from "@chakra-ui/react";
import { useNavigate } from "react-router-dom";
import { SearchIcon } from "@chakra-ui/icons";
import Nav from "../Components/Navlink";
Состояния:
Здесь мы объявили наши состояния
//States
const [data, setData] = useState([]);
const [data2, setData2] = useState([]);
const [searchInput, setSearchInput] = useState("");
const [selectInput, setSelectInput] = useState("all");
let navigate = useNavigate();
Api:
Вызов Api
//Calling Apis
useEffect(() => {
if (selectInput === "all") {
fetch(`https://restcountries.com/v3.1/all`)
.then((res) => res.json())
.then((data) => {
return (
setData(data),
setData2(data))
})
.catch((err) => console.log("Error:", err.message));
} else {
fetch(`https://restcountries.com/v3.1/region/${selectInput}`)
.then((res) => res.json()).then((data)=>{
return (
setData(data),
setData2(data)
)
})
.catch((err) => console.log("Error:", err.message));
}
}, [selectInput]);
Выбор региона и функции поиска страны
//Handle Region select
const handleChangeSelect = (e) => {
setSelectInput(e.target.value);
};
//Handle Country Search
const handleChangeInput = (e) => {
e.preventDefault();
setSearchInput(e.target.value);
setData(
data2.filter((x) =>
x?.name?.common
?.toLowerCase()
?.includes(e?.target?.value?.toLowerCase())
)
);
};
Импортная панель навигации
<Nav/>
Country Search and Region Select form
<form>
<Flex pr="50" pl="50" flexWrap={"wrap"}>
<Box p="4">
<InputGroup>
<InputLeftElement
pointerEvents="none"
children={<SearchIcon color="gray.300" />}
/>
<Input
value={searchInput}
onChange={handleChangeInput}
type="text"
placeholder="Search for a country "
/>
</InputGroup>
</Box>
<Spacer />
<Box p="4">
<Select onChange={handleChangeSelect} placeholder="Select option">
<option value="all">All</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>
</Box>
</Flex>
</form>
Рендеринг данных
{data2?.length === 0 ? (
<Progress colorScheme="pink" size="xs" isIndeterminate />
) : (
<Box w="100%">
<SimpleGrid
columns={[1, null, 4]}
spacing={10}
pt="100"
pr="50"
pl="50"
>
{data?.map((x) => (
<GridItem
key={x?.name?.common}
onClick={() =>
navigate(`/singlecountry/${x?.cca2?.toLowerCase()}`, {})
}
>
<Box
maxW="sm"
borderWidth="1px"
borderRadius="lg"
overflow="hidden"
>
<Image
src={x?.flags?.svg}
alt={x?.name?.common}
height="200px"
width="100%"
/>
<Box p="6">
<Box
mt="1"
fontWeight="semibold"
as="h4"
lineHeight="tight"
noOfLines={1}
>
{x?.name?.common}
</Box>
<Box
mt="1"
fontWeight="semibold"
as="h4"
lineHeight="tight"
noOfLines={1}
>
Population: {x?.population}
</Box>
<Box
mt="1"
fontWeight="semibold"
as="h4"
lineHeight="tight"
noOfLines={1}
>
Region: {x?.region}
</Box>
<Box
mt="1"
fontWeight="semibold"
as="h4"
lineHeight="tight"
noOfLines={1}
>
Capital: {x?.capital}
</Box>
</Box>
</Box>
</GridItem>
))}
</SimpleGrid>
</Box>
)}
Компонент одной страны
Теперь мы создадим компонент одной страны.
Когда пользователь нажимает на страну, на отдельной странице открывается подробная информация о ней. Все, что мы здесь делаем, — это устанавливаем состояние и затем обновляем состояние с помощью ответа, полученного от API внутри use effect. Затем мы отображаем состояние страны в компонентах Chakra.
import React, { useEffect, useState } from "react";
import { useParams, useNavigate } from "react-router-dom";
import {
Button,
Center,
GridItem,
Image,
Progress,
SimpleGrid,
} from "@chakra-ui/react";
import { Box } from "@chakra-ui/react";
import Nav from "../Components/Navlink";
function SingleCountry() {
let { countryname } = useParams();
const [data, setData] = useState();
let navigate = useNavigate();
useEffect(() => {
fetch(`https://restcountries.com/v3.1/alpha/${countryname}`)
.then((res) => res.json())
.then((data) => setData(data))
.catch((err) => console.log("Error:", err.message));
}, [countryname]);
return (
<div>
<Nav />
<Box onClick={() => navigate(-1)} p={'10'} >
<Button size="lg" variant="solid" mr="3">
Back
</Button>
</Box>
{data === undefined || data === null ? (
<Progress colorScheme="pink" size="xs" isIndeterminate />
) : (
data?.map((x) => {
return (
<Center key={x?.name?.common} >
<SimpleGrid
columns={[1, null, 2]}
spacing={100}
pt="100"
pr="50"
pl="50"
>
<GridItem w="100%">
<Image src={x?.flags?.svg} alt={x?.Region} height="350" />
</GridItem>
<GridItem w="100%">
<Box
mt="1"
fontWeight="semibold"
as="h4"
lineHeight="tight"
noOfLines={1}
>
{x?.name?.common}
</Box>
<SimpleGrid columns={2} spacing={10}>
<Box>Native Name: {x?.name?.common}</Box>
<Box>Top Level Domain: {x?.tld[0]}</Box>
</SimpleGrid>
<SimpleGrid columns={2} spacing={10}>
<Box>Population: {x?.population}</Box>
<Box>
Currencies:{" "}
{x?.currencies[Object?.keys(x?.currencies)[0]]?.name}
</Box>
</SimpleGrid>
<SimpleGrid columns={2} spacing={10}>
<Box>Region: {x?.region}</Box>
<Box>
Language(s): {x?.languages[Object.keys(x?.languages)[0]]}
</Box>
</SimpleGrid>
<SimpleGrid columns={2} spacing={10}>
<Box>Subregion: {x?.subregion}</Box>
</SimpleGrid>
<SimpleGrid columns={2} spacing={10}>
<Box>Capital: {x?.capital}</Box>
</SimpleGrid>
<SimpleGrid mt="50" columns={2} spacing={10}>
<Box>Border Countries:</Box>
<Box>
{x?.borders?.map((x) => (
<Button
onClick={() => navigate(`/singlecountry/${x}`)}
size="lg"
key={x}
variant="solid"
mr="3"
>
{x}
</Button>
))}
</Box>
</SimpleGrid>
</GridItem>
</SimpleGrid>
</Center>
);
})
)}
</div>
);
}
export default SingleCountry;
Давайте создадим сердце этого проекта.
App.tsx
import * as React from "react"
import { ChakraProvider, theme } from '@chakra-ui/react'
import { BrowserRouter } from "react-router-dom";
import Routing from "./Components/Routing";
export const App = () => (
<div>
<ChakraProvider theme={theme}>
<BrowserRouter>
<Routing/>
</BrowserRouter>
</ChakraProvider>
</div>
)
Создав все наши компоненты, давайте войдем в каталог нашего приложения и запустим npm start
, чтобы запустить приложение. В этот момент мы должны увидеть что-то похожее на изображение ниже.
Если мы нажмем на любую из стран, она должна перенаправить нас на другую страницу, где мы сможем увидеть более подробную информацию о стране, как показано на рисунке ниже.
Что ж, поздравляем вас с этим замечательным успехом! В вашем распоряжении готовое решение для REST API стран с переключателем цветовых тем.
Спасибо за прочтение🌟🎉
Рад видеть, что вам понравилась статья. Пожалуйста, дайте мне знать, что вы думаете в разделе комментариев.
В другой блог, в другой день, до тех пор Femi👋.