В этом первом учебнике по React мы собираемся сделать «лабораторию на руках», потому что, во-первых, он будет на португальском языке, а во-вторых, он будет максимально практичным, не углубляясь в каждую используемую здесь функцию. В конце этой статьи вы узнаете (или не узнаете) некоторые понятия
- Создайте компоненты;
- Использование фильтров;
- Используйте пейджинг;
- Используйте хуки реакций (useState, useEffect);
- Создавайте динамические маршруты;
- Создайте панель поиска, навигацию и т.д;
Примечание: Несмотря на то, что статья написана на португальском языке, весь код будет написан на английском из соображений хорошей практики.
Так что поехали!
Создайте папку с именем ‘react-wiki’;
Откройте эту папку в VSCODE;
Теперь откройте терминал (CTRL + ‘) и выполните следующие команды:
1- NPX — это программа для запуска пакетов от NPM. Он запускает библиотеки, которые можно загрузить с сайта npmjs.
2- Bootstrap — это front-end фреймворк, который предоставляет CSS-структуры для быстрого и простого создания отзывчивых веб-сайтов и приложений.
3- Popper. js — это библиотека JavaScript, которая служит для позиционирования таких элементов, как меню, всплывающие подсказки и всплывающие окна.
npm install @popperjs/core --save
4- SASS — это язык расширения CSS, аббревиатура расшифровывается как «Syntactically Awesome Style Sheets», что дословно переводится как таблицы стилей с удивительным синтаксисом. Его идея заключается в добавлении специальных возможностей, таких как переменные, функции, операции и другие вещи.
5- React Router — это библиотека React, которая позволяет осуществлять навигацию между различными частями приложения, например, страницами.
6- React Paginate — это компонент, который будет выполнять всю пагинацию. В этой статье я только покажу, как это реализовать, не вдаваясь в логику того, как это работает.
И, наконец, запустите приложение, чтобы проверить, все ли в порядке:
Все работает? Если да, то вы должны были увидеть логотип ReactJs, вращающийся на экране, и, вероятно, он открылся по адресу «http://localhost:3000».
Приложение, которое мы собираемся разработать, будет представлять собой «вики» персонажей из мультфильма «Рик и Морти», и для этого мы будем использовать публичный api, который находится по этому адресу https://rickandmortyapi.com.
Всю документацию и как использовать api можно посмотреть в разделе Docs или на сайте https://rickandmortyapi.com/documentation.
Наше приложение будет иметь навигационное меню, строку поиска, фильтры по статусу, полу, видам, эпизодам и местоположению.
Вот картинка, чтобы увидеть, как будет выглядеть окончательный вариант применения. В конце статьи я оставлю ссылку на запущенное приложение, чтобы вы могли проверить его более подробно.
Теперь первым шагом будет создание структуры папок для организации приложения.
Создайте структуру папок, как показано ниже:
src > components >
- Карта
- Фильтр
- Навбар
- Пагинация
- Поиск
Мы собираемся удалить все содержимое из файла App.css и превратить его в файл SASS, просто переименуем его в App.scss;
Внутри этого файла у нас будет только один css-класс, но мы привыкнем использовать его как sass:
.active {
color: #0b5ed7 !important;
font-weight: bold;
border-bottom: 3px solid #0b5ed7;
}
Внутри файла App.js мы изначально импортируем bootstrap css, bootstrap js и хуки useState и useEffect из react. Ваши первые строки кода в файле App.js:
import "bootstrap/dist/css/bootstrap.min.css";
import "bootstrap/dist/js/bootstrap";
import React, { useState, useEffect } from "react";
Прежде чем продолжить, давайте создадим карточку для отображения результата работы api.
«Карточка
Теперь мы создадим карточку для отображения результата работы api, который мы будем использовать. Результат будет таким, как показано на рисунке ниже:
Сначала мы создадим файл Card.module.scss в папке Card. Он будет содержать css карты и будет интерпретироваться sass.
Вставьте приведенный ниже код:
$radius: 10px;
.card {
border: 2px solid #0b5ed7;
border-radius: $radius;
}
.content {
padding: 10px;
}
.img {
border-radius: $radius $radius 0 0;
}
.badge {
top: 10px;
right: 20px;
font-size: 17px;
}
Для тех, кто никогда не видел и не использовал сас. Использование sass можно проверить в переменной $radius. Присваивая значение $radius, мы можем присвоить его непосредственно как значение для каждого свойства в остальной части css, и при изменении значения переменной оно будет меняться для всех свойств сразу, как мы это делаем, например, в javascript.
Внутри папки Card создайте файл javascript Card.js и добавьте в него следующий код:
import React from "react";
import styles from "./Card.module.scss";
const Card = ({ page, results }) => {
let display;
if (results) {
display = results.map((x) => {
let { id, image, name, status, location } = x;
return (
<div
key={id}
className="col-lg-4 col-md-6 col-sm-6 col-12 mb-4 position-relative text-dark"
>
<div className={`${styles.card} d-flex flex-column justify-content-center`}>
<img className={`${styles.img} img-fluid`} src={image} alt="" />
<div className={`${styles.content}`}>
<div className="fs-5 fw-bold mb-4">{name}</div>
<div className="">
<div className="fs-6 fw-normal">Last Location</div>
<div className="fs-5">{location.name}</div>
</div>
</div>
</div>
</div>
);
});
}
else{
display = "Nenhum personagem encontrado :/";
}
return <>{display}</>;
}
export default Card;
Функция Card принимает два параметра «page» и «results». Он сопоставляет результаты и извлекает свойства, которые мы будем использовать, такие как id, изображение, имя и т.д. В «return» мы помещаем html, который хотим отобразить. Обратите внимание на некоторые особенности, например, вместо использования «class» в ReactJS мы используем «className» для назначения css-класса.
А чтобы использовать класс из файла scss, мы делаем то, что мы называем интерполяцией и присваиваем его с «styles» перед классом, т.е. если вы хотите использовать класс «card» в div, вы должны поставить что-то подобное этому:
<div className={`${styles.card}`} >conteudo da div</div>
.
Теперь подготовьте наш App.js и добавьте приведенный ниже код для импорта карты:
и внутри «function App()» мы будем использовать хуки useState и useEffect, добавьте следующий код:
let [fetchedData, updateFetchedData] = useState([]);
let { info, results } = fetchedData;
let api = `https://rickandmortyapi.com/api/character/?page=1`;
useEffect(() => {
(async function () {
let data = await fetch(api).then((res) => res.json());
updateFetchedData(data);
})();
}, [api]);
return (
<div className="App">
<h1 className="text-center mb-3">Personagens</h1>
<div className="container">
<div className="row">
Filtro aparecerá aqui
<div className="col-lg-8 col-12">
<div className="row">
<Card page="/" results={results} />
</div>
</div>
</div>
</div>
</div>
);
Здесь мы выполнили вызов api и теперь увидим результат заполнения наших карточек.
Готовый файл App.js должен выглядеть следующим образом:
import 'bootstrap/dist/css/bootstrap.min.css';
import "bootstrap/dist/js/bootstrap";
import React, { useState, useEffect } from "react";
import Card from "./components/Card/Card";
function App() {
let [fetchedData, updateFetchedData] = useState([]);
let { info, results } = fetchedData;
let api = `https://rickandmortyapi.com/api/character/?page=1`;
useEffect(() => {
(async function () {
let data = await fetch(api).then((res) => res.json());
updateFetchedData(data);
})();
}, [api]);
return (
<div className="App">
<h1 className="text-center mb-3">Personagens</h1>
<div className="container">
<div className="row">
Filtro aparecerá aqui
<div className="col-lg-8 col-12">
<div className="row">
<Card page="/" results={results} />
</div>
</div>
</div>
</div>
</div>
);
}
export default App;
Результат должен выглядеть следующим образом:
Теперь давайте добавим индикатор состояния символа в нашу карточку, для этого поместите следующий код в return функции Card в файле Card.js:
{(() => {
if (status === "Dead") {
return (
<div
className={`${styles.badge} position-absolute badge bg-danger`}
>
{status}
</div>
);
} else if (status === "Alive") {
return (
<div
className={`${styles.badge} position-absolute badge bg-success`}
>
{status}
</div>
);
} else {
return (
<div
className={`${styles.badge} position-absolute badge bg-secondary`}
>
{status}
</div>
);
}
})()}
Приведенный выше код проверяет статус и добавляет «значок» цвета статуса для каждой карточки.
Готовый файл Card.js будет выглядеть следующим образом:
import React from "react";
import styles from "./Card.module.scss";
const Card = ({ page, results }) => {
let display;
if (results) {
display = results.map((x) => {
let { id, image, name, status, location } = x;
return (
<div
key={id}
className="col-lg-4 col-md-6 col-sm-6 col-12 mb-4 position-relative text-dark"
>
<div className={`${styles.card} d-flex flex-column justify-content-center`}>
<img className={`${styles.img} img-fluid`} src={image} alt="" />
<div className={`${styles.content}`}>
<div className="fs-5 fw-bold mb-4">{name}</div>
<div className="">
<div className="fs-6 fw-normal">Last Location</div>
<div className="fs-5">{location.name}</div>
</div>
</div>
</div>
{(() => {
if (status === "Dead") {
return (
<div
className={`${styles.badge} position-absolute badge bg-danger`}
>
{status}
</div>
);
} else if (status === "Alive") {
return (
<div
className={`${styles.badge} position-absolute badge bg-success`}
>
{status}
</div>
);
} else {
return (
<div
className={`${styles.badge} position-absolute badge bg-secondary`}
>
{status}
</div>
);
}
})()}
</div>
);
});
}
else{
display = "Nenhum personagem encontrado :/";
}
return <>{display}</>;
}
export default Card;
Результат этого изменения будет выглядеть следующим образом:
» Поиск
Следующим шагом будет создание компонента Search для поиска карточки по введенному термину, как показано на рисунке ниже:
Сначала создадим 2 хука типа useState, чтобы помочь нам в поиске.
Внутри App.js создайте хуки в соответствии с приведенным ниже кодом:
Нам нужно будет изменить url api (в файле App.js) для получения параметров, которые мы будем сообщать в дальнейшем.
Измените это:
let api = `https://rickandmortyapi.com/api/character/?page=1`;
для этого:
let api = `https://rickandmortyapi.com/api/character/?page=${pageNumber}&name=${search}`;
Внутри папки Sidebar создайте два файла, Sidebar.js и Search.module.scss, и поместите в них приведенный ниже код:
.input {
width: 40%; border-radius: 8px;
border: 2px solid #0b5ed7;
box-shadow: 1px 3px 9px rgba($color: #000000, $alpha: 0.25);
padding: 10px 15px;
&:focus { outline: none; }
}
.btn {
box-shadow: 1px 3px 9px rgba($color: #000000, $alpha: 0.25);
}
@media (max-width: 576px) {
.input { width: 80%; }
}
Этот css будет стилизовать нашу боковую панель и кнопку поиска.
Теперь давайте поместим код Search.js, как показано ниже:
import React from "react";
import styles from "./Search.module.scss";
const Search = ({ setSearch, updatePageNumber }) => {
let searchBtn = (e) => {
e.preventDefault();
};
return (
<form
className={`${styles.search} d-flex flex-sm-row flex-column align-items-center justify-content-center gap-4 mb-5`}
>
<input
onChange={(e) => {
updatePageNumber(1);
setSearch(e.target.value);
}}
placeholder="Pesquisar por personagens..."
className={styles.input}
type="text"
/>
<button
onClick={searchBtn}
className={`${styles.btn} btn btn-primary fs-5`}
>
Search
</button>
</form>
);
};
export default Search;
Приведенный выше код заставляет щелчок по кнопке, как и ввод текста в текстовом поле, выполнять поиск.
Теперь давайте импортируем компонент Search в наш App.js. Вставьте строку кода чуть ниже импорта Card:
Все еще в App.js мы размещаем следующий фрагмент кода чуть ниже H1 с заголовком страницы:
Изменения внесены! Теперь мы можем протестировать его, просто набрав в строке поиска, мы увидим результат.
«Пагинация
Пагинация! Пора перевести все на страницу!
И для этого мы будем использовать компонент под названием react-paginate.
Всю документацию и дополнительную информацию можно найти по этой ссылке https://www.npmjs.com/package/react-paginate. Цель данной статьи — не углубиться в этот компонент, а показать его применение на практике.
Так что поехали!
Внутри папки Pagination создайте файл Pagination.js. В нем мы разместим следующий код для создания нашей пагинации:
import React, { useState, useEffect } from "react";
import ReactPaginate from "react-paginate";
const Pagination = ({ pageNumber, info, updatePageNumber }) => {
let pageChange = (data) => {
updatePageNumber(data.selected + 1);
};
const [width, setWidth] = useState(window.innerWidth);
const updateDimensions = () => {
setWidth(window.innerWidth);
};
useEffect(() => {
window.addEventListener("resize", updateDimensions);
return () => window.removeEventListener("resize", updateDimensions);
}, []);
return (
<>
<style jsx>
{`
@media (max-width: 768px) {
.pagination {
font-size: 12px;
}
.next,
.prev {
display: none;
}
}
@media (max-width: 768px) {
.pagination {
font-size: 14px;
}
}
`}
</style>
<ReactPaginate
className="pagination justify-content-center my-4 gap-4"
nextLabel="Next"
forcePage={pageNumber === 1 ? 0 : pageNumber - 1}
previousLabel="Prev"
previousClassName="btn btn-primary fs-5 prev"
nextClassName="btn btn-primary fs-5 next"
activeClassName="active"
marginPagesDisplayed={width < 576 ? 1 : 2}
pageRangeDisplayed={width < 576 ? 1 : 2}
pageCount={info?.pages}
onPageChange={pageChange}
pageClassName="page-item"
pageLinkClassName="page-link"
/>
</>
);
};
export default Pagination;
В приведенном выше коде у нас есть функция, которая получает параметры «pageNumber», «info» и «updatePageNumber» и обновляет данные в соответствии со страницей. ReactPaginate имеет некоторые свойства, которые можно проверить в документации на сайте компонента.
В App.js мы поместим следующий фрагмент кода прямо рядом с закрытием последнего div:
<Pagination info={info} pageNumber={pageNumber} updatePageNumber={updatePageNumber}/>
На этом этапе ваш файл App.js должен выглядеть следующим образом:
import 'bootstrap/dist/css/bootstrap.min.css';
import "bootstrap/dist/js/bootstrap";
import React, { useState, useEffect } from "react";
import Card from "./components/Card/Card";
import Search from "./components/Search/Search"
import Pagination from "./components/Pagination/Pagination"
function App() {
let [fetchedData, updateFetchedData] = useState([]);
let { info, results } = fetchedData;
let [pageNumber, updatePageNumber] = useState(1);
let [search, setSearch] = useState("");
let api = `https://rickandmortyapi.com/api/character/?page=${pageNumber}&name=${search}`;
useEffect(() => {
(async function () {
let data = await fetch(api).then((res) => res.json());
updateFetchedData(data);
})();
}, [api]);
return (
<div className="App">
<h1 className="text-center mb-3">Personagens</h1>
<Search setSearch={setSearch} updatePageNumber={updatePageNumber} />
<div className="container">
<div className="row">
Filtro aparecerá aqui
<div className="col-lg-8 col-12">
<div className="row">
<Card page="/" results={results} />
</div>
</div>
</div>
</div>
<Pagination
info={info}
pageNumber={pageNumber}
updatePageNumber={updatePageNumber}
/>
</div>
);
}
export default App;
Результат должен выглядеть так, как показано на рисунке ниже:
Продолжение…
Часть 2