Разбиение на страницы, также называемое пейджингом, делит информацию на веб-странице на разные страницы, по которым можно перемещаться с помощью кнопок или нумерованного списка. Разбивка на страницы может улучшить организацию вашего веб-сайта, улучшить UX и, в конечном итоге, повысить общий рейтинг. Вы можете добавить нумерацию страниц в веб-приложение, настольное приложение или мобильное приложение и реализовать его либо на стороне клиента, либо на стороне сервера.

В этом руководстве мы будем использовать React и Tailwind CSS для создания двух разных типов компонентов разбивки на страницы на стороне клиента. Один будет использовать кнопки для навигации, а другой будет использовать нумерованный список. Давайте начнем!

Использование Tailwind CSS в проекте React

Сначала настройте новый проект React на локальном компьютере, выполнив следующую команду:

npx create-react-app my-app
cd my-app

Далее мы установим Tailwind CSS и несколько других зависимостей в наш каталог:

npm install -D tailwindcss@npm:@tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9

Нам нужно установить приложение Create React, однако оно не может изначально переопределить конфигурацию PostCSS. Поэтому мы установим CRACO, уровень конфигурации для CRA, в дополнение к нескольким другим скриптам в нашем package.json:

npm install @craco/craco

Раздел scripts вашего проекта должен выглядеть как блок кода ниже:

"scripts": {
    "start": "craco start",
    "build": "craco build",
    "test": "craco test",
    "eject": "react-scripts eject"
  },

Теперь давайте создадим новый файл с именем craco.config.js и добавим Tailwind CSS и autoprefixer в качестве плагинов PostCSS:

module.exports = {
  style: {
    postcss: {
      plugins: [require("tailwindcss"), require("autoprefixer")],
    },
  },
};

Далее мы создадим файл с именем tailwind.config.js с помощью следующей команды:

npx tailwindcss-cli@latest init

Чтобы завершить настройку Tailwind CSS, добавьте следующий код в файл default index.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

Теперь, когда наш проект полностью настроен, мы можем приступить к созданию компонентов разбиения на страницы!

Структурирование компонента пагинации

Наш проект будет следовать структуре на изображении ниже. Давайте подробно рассмотрим некоторые из этих файлов и папок:

Pagination.js будет содержать логику для обработки и отображения компонента нумерации страниц. Код в этой папке будет отличаться для каждого из двух типов нумерации страниц, поэтому мы еще раз рассмотрим его позже.

Posts.js — это статический элемент POST, который будет получать случайные данные из API. Данные и структура Posts.js, как показано в блоке кода ниже, будут одинаковыми для обоих типов разбиения на страницы:

import React from "react";
const Posts = ({ posts, loading }) => {
  if (loading) {
    return <h2>Loading...</h2>;
  }
  return (
    <div>
      <ul>
        {posts.map((post) => (
          <li
            key={post.id}
            className='text-gray-700 font-semibold text-xl mb-2 border p-2'
          >
            {post.title}
          </li>
        ))}
      </ul>
    </div>
  );
};
export default Posts;

Разбиение на страницы с помощью кнопок навигации

Первый тип компонента разбивки на страницы, который мы создадим, использует кнопки «Далее» и «Назад» для навигации по данным на веб-странице. Наш файл App.js будет выглядеть как блок кода ниже:

import React, { useState, useEffect } from "react";
import Posts from "./Posts";
import Pagination from "./Pagination";
import axios from "axios";
const App = () => {
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(false);
  const [currentPage, setCurrentPage] = useState(1);
  const [postsPerPage] = useState(10);
  useEffect(() => {
    const fetchPosts = async () => {
      setLoading(true);
      const res = await axios.get("https://jsonplaceholder.typicode.com/posts");
      setPosts(res.data);
      setLoading(false);
    };
    fetchPosts();
  }, []);
  // Get current posts
  const indexOfLastPost = currentPage * postsPerPage;
  const indexOfFirstPost = indexOfLastPost - postsPerPage;
  const currentPosts = posts.slice(indexOfFirstPost, indexOfLastPost);
  // Change page
  const paginateFront = () => setCurrentPage(currentPage + 1);
  const paginateBack = () => setCurrentPage(currentPage - 1);
  return (
    <div>
      <Posts posts={currentPosts} />
      <Pagination
        postsPerPage={postsPerPage}
        totalPosts={posts.length}
        paginateBack={paginateBack}
        paginateFront={paginateFront}
        currentPage={currentPage}
      />
    </div>
  );
};
export default App;

В нашем файле App.js данные в posts поступают из бэкенда. Давайте рассмотрим некоторые функции в нашем компоненте разбивки на страницы, которые используют эти данные:

  • currentPage: указывает пользователю, на какой странице он находится в данный момент.
  • postsPerPage: общее количество сообщений, которые будут отображаться на странице.
  • currentPosts: массив сообщений для текущей страницы

Чтобы получить currentPosts, нам нужно передать indexOfFirstPost и indexOfLastPost функции slice().

Для перемещения вперед и назад между страницами мы будем использовать paginateFront и paginateBack. Эти функции просто увеличивают или уменьшают currentPage, и в результате вычисляется currentPosts.

Pagination.js файл

Теперь давайте посмотрим на наш файл Pagination.js:

import React from "react";
export default function Pagination({
  postsPerPage,
  totalPosts,
  paginateFront,
  paginateBack,
  currentPage,
}) {

  return (
    <div className='py-2'>
      <div>
        <p className='text-sm text-gray-700'>
          Showing
          <span className='font-medium'>{currentPage * postsPerPage - 10}</span>
          to
          <span className='font-medium'> {currentPage * postsPerPage} </span>
          of
          <span className='font-medium'> {totalPosts} </span>
          results
        </p>
      </div>
      <nav className='block'></nav>
      <div>
        <nav
          className='relative z-0 inline-flex rounded-md shadow-sm -space-x-px'
          aria-label='Pagination'
        >
          <a
            onClick={() => {
              paginateBack();
            }}
            href='#'
            className='relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50'
          >
            <span>Previous</span>
          </a>
          <a
            onClick={() => {
              paginateFront();
            }}
            href='#'
            className='relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50'
          >
            <span>Next</span>
          </a>
        </nav>
      </div>
    </div>
  );
}

Компонент разбивки на страницы принимает реквизиты, которые отображают текущую информацию о нашей странице, а служебные классы Tailwind CSS устраняют необходимость во внешнем CSS.

Когда мы запустим приведенный выше код, мы получим следующий результат:

На изображении выше наш компонент разбивки на страницы отображает от 10 до 20 результатов из 100. Давайте нажмем кнопку «Далее», чтобы посмотреть, что произойдет:

Теперь мы можем видеть результаты с 20 по 30. Когда мы нажмем кнопку «Назад», мы вернемся к результатам с 10 по 20:

Разбивка на страницы с помощью нумерованного списка

Второй компонент разбивки на страницы, который мы создадим, использует для навигации нумерованный список вместо кнопок «Далее» и «Назад». Нам нужно внести несколько изменений в наш файл App.js и реквизиты, которые отправляются в компонент разбивки на страницы.

Обновите файл App.js, чтобы он выглядел как блок кода ниже:

import React, { useState, useEffect } from "react";
import Posts from "./Posts";
import Pagination from "./Pagination";
import axios from "axios";
const App = () => {
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(false);
  const [currentPage, setCurrentPage] = useState(1);
  const [postsPerPage] = useState(10);
  useEffect(() => {
    const fetchPosts = async () => {
      setLoading(true);
      const res = await axios.get("https://jsonplaceholder.typicode.com/posts");
      setPosts(res.data);
      setLoading(false);
    };
    fetchPosts();
  }, []);
  // Get current posts
  const indexOfLastPost = currentPage * postsPerPage;
  const indexOfFirstPost = indexOfLastPost - postsPerPage;
  const currentPosts = posts.slice(indexOfFirstPost, indexOfLastPost);
  // Change page
  const paginate = (pageNumber) => setCurrentPage(pageNumber);
  return (
    <div>
      <Posts posts={currentPosts} />
      <Pagination
        postsPerPage={postsPerPage}
        totalPosts={posts.length}
        paginate={paginate}
        currentPage={currentPage}
      />
    </div>
  );
};
export default App;

Теперь у нас есть одна функция разбивки на страницы, которая только обновляет currentPage для установки currentPosts, в отличие от передачи indexOfFirstPost и indexOfLastPost, как мы делали для нашего предыдущего компонента разбиения на страницы.

Давайте посмотрим на код нашего компонента разбивки на страницы, который использует нумерованный список:

import React from "react";
export default function Pagination({
  postsPerPage,
  totalPosts,
  paginate,
  currentPage,
}) {
  const pageNumbers = [];
  for (let i = 1; i <= Math.ceil(totalPosts / postsPerPage); i++) {
    pageNumbers.push(i);
  }
  return (
    <div className='py-2'>
      <div>
        <p className='text-sm text-gray-700'>
          Showing
          <span className='font-medium'>
            {" "}
            {currentPage * postsPerPage - 10}{" "}
          </span>
          to
          <span className='font-medium'> {currentPage * postsPerPage} </span>
          of
          <span className='font-medium'> {totalPosts} </span>
          results
        </p>
      </div>
      <nav className='block'>
        <ul className='flex pl-0 rounded list-none flex-wrap'>
          <li>
            {pageNumbers.map((number) => (
              <a
                onClick={() => {
                  paginate(number);
                }}
                href='#'
                className={
                  currentPage === number
                    ? "bg-blue border-red-300 text-red-500 hover:bg-blue-200 relative inline-flex items-center px-4 py-2 border text-sm font-medium"
                    : "bg-white border-gray-300 text-gray-500 hover:bg-blue-200 relative inline-flex items-center px-4 py-2 border text-sm font-medium"
                }
              >
                {number}
              </a>
            ))}
          </li>
        </ul>
      </nav>
    </div>
  );
}

Мы создали массив, который динамически вычисляет количество страниц, необходимых для заданного объема данных. Затем он добавляет данные внутрь массива pageNumbers.

Теперь мы создадим неупорядоченный список, который отображает элементы списка, перебирая наш массив pageNumbers. Создание неупорядоченного списка создаст компонент навигации для страниц, который будет выглядеть следующим образом:

Внутри функции map мы прикрепили обработчик кликов к каждому номеру страницы. При нажатии каждая кнопка будет переходить на определенную страницу. currentPage будет установлено в нашем файле App.js, и мы получим обновленный массив currentPosts, который будет отображать требуемый контент во внешнем интерфейсе.

Давайте выделим активную страницу внутри нашего компонента навигации по страницам:

<a
    onClick={() => {
      paginate(number);
    }}
    href='#'
    className={
      currentPage === number
        ? "bg-blue border-red-300 text-red-500 hover:bg-blue-200 relative inline-flex items-center px-4 py-2 border text-sm font-medium"
        : "bg-white border-gray-300 text-gray-500 hover:bg-blue-200 relative inline-flex items-center px-4 py-2 border text-sm font-medium"
    }
  >
  {number}
</a>

Для тега <a> мы устанавливаем разные классы CSS Tailwind на основе проверки. Если pageNumber для нашего тега <a> равно currentPage, мы будем отличать его от других тегов <a>, придав ему красную рамку и цвет шрифта.

Давайте снова запустим проект, чтобы увидеть результат. На первой странице мы увидим следующее:

Когда мы перейдем на десятую страницу, мы увидим следующее:

Заключение

Теперь у вас должно быть полное представление о нумерации страниц! Разбивка на страницы — отличная функция для улучшения UX вашего приложения. Мы рассмотрели два метода реализации разбивки на страницы в приложении React с помощью Tailwind CSS, кнопок навигации и нумерованного списка. Лучший выбор будет зависеть от характера вашего приложения и ваших данных.

Дополнительные материалы на PlainEnglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord.