React JS — Создание вики персонажей Рики и Морти — часть 1

В этом первом учебнике по 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

Оцените статью
devanswers.ru
Добавить комментарий