DEV Community

Ivan Trindade
Ivan Trindade

Posted on

4 3

VAMOS CONSTRUIR: Um componente de paginação React!

Olá, dev! Hoje, vou orientá-lo através de um componente de paginação simples que você pode construir e implementar com facilidade, chamar de seu, mostrá-lo, sufocar seus entes queridos com ele, as oportunidades são infinitas.

O que é paginação?

Para quem não sabe, é um meio de exibir apenas parte de uma quantidade de dados em uma página e dar ao usuário a capacidade de ir de uma página para outra de resultados de pesquisa, para que não sejam todos renderizados ao mesmo tempo.
Ele contribui para uma melhor experiência do usuário e está em conformidade com as melhores práticas.

Configurar

Vamos criar um aplicativo React digitando no terminal:

Usando npm:

npx create-react-app pagination --template typescript
Enter fullscreen mode Exit fullscreen mode

ou

Usando yarn:

yarn create react-app pagination --template typescript
Enter fullscreen mode Exit fullscreen mode

Acesse o diretório do projeto e abra-o com o seu editor favorito:

cd pagination
code .
Enter fullscreen mode Exit fullscreen mode

No meu caso, o último comando abrirá o Visual Studio Code no diretório atual. Talvez você precisa configurar seu VSCode, para conseguir abrir com o comando code.

Agora é hora de instalar todas as dependências legais sem as quais eu nunca trabalharia! Irei usar o Styled Components e também vou instalar o faker, para fazer alguns dados simulados para este exemplo. E por último, também irei instalar o react-select para um componente suspenso na minha paginação e react-icons para alguns ícones:

Irei usar o yarn para instalar, mas você também poderá usar o npm:

yarn add styled-components @types/styled-components @faker-js/faker react-select react-icons
Enter fullscreen mode Exit fullscreen mode

Imagem do código
Eu gosto de remover todos os arquivos .css, exceto index.css, e em index.css eu gosto de colocar um reset css:

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
Enter fullscreen mode Exit fullscreen mode

Construindo os componentes

Você pode ter paginação de front-end ou back-end. Para este exemplo, vou simular uma chamada de back-end em um objeto de dados.
Eu crio um diretório interfaces/types.ts para armazenar todas as minhas tipagens no Typescript:

export interface User {
    id: number;
    name: string;
    email: string;
}

export interface PaginationProps {
    limit: number;
    email: string;
}

export interface Pagination extends PaginationProps {
    total: number;
}

export interface PaginationData<T extends object> {
    pagination: Pagination;
    data: T[];
}
Enter fullscreen mode Exit fullscreen mode

Então eu crio um arquivo getTableData.ts:

import { faker } from "@faker-js/faker";
import { PaginationData, PaginationProps } from "./types";


export const getTableData = <T extends object>({
    limit,
    offset,
}: PaginationProps): PaginationData<T> => {
    const data = Array(1000)
        .fill('')
        .map((_, id) => ({
            id,
            name: faker.name.findName(),
            email: faker.internet.email(),
        }))
        .slice(offset, limit + offset) as T[];
    return {
        pagination: { limit, offset, total: 1000 },
        data,
    }
}
Enter fullscreen mode Exit fullscreen mode

O que estou fazendo aqui é aceitar as variáveis limit e offset, me dizendo como dividir os dados para retornar um subconjunto deles.
Eu crio um arquivo styles.ts e construo algum estilo de componente:

import styled from "styled-components";

const Container = styled.div`
  width: 100vw;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
`;

const TableContainer = styled.div`
  width: 600px;
  height: 400px;
  overflow: auto;
`;

const Table = styled.table`
  width: 500px;
  border-collapse: collapse;
  position: relative;
  & th {
    text-align: left;
    background: #282560;
    font-weight: bold;
    color: white;
    border: 1px solid white;
    position: sticky;
  }
  & th,
  & td {
    padding: 0.3rem;
    font-size: 0.7rem;
  }
  & tbody tr:nth-child(even) {
    & td {
      background: #edeef6;
    }
  }
`;

export { Container, Table, TableContainer };
Enter fullscreen mode Exit fullscreen mode

E então, irei condificar meu App.tsx:

import { useEffect, useState } from "react";
import { Container, Table, TableContainer } from "./styles";
import { PaginationData, User } from "./interfaces/types";

function App() {
  const [data, setData] = useState<PaginationData<User>>();
  const [limit, setLimit] = useState(10);
  const [offset, setOffset] = useState(0);

  useEffect(() => {
    const getData = async () => {
      const tableData = (await import("./getTableData")).default<User>({
        limit,
        offset,
      });
      setData(tableData);
    };
    getData();
  }, [limit, offset]);

  return (
    <Container>
      <TableContainer>
        <Table>
          <thead>
            <tr>
              <th>ID</th>
              <th>Name</th>
              <th>Email</th>
            </tr>
          </thead>
          <tbody>
            {data?.data.map((user) => {
              return (
                <tr key={user.id}>
                  <td>{user.id}</td>
                  <td>{user.name}</td>
                  <td>{user.email}</td>
                </tr>
              );
            })}
          </tbody>
        </Table>
      </TableContainer>
    </Container>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

Eu criei duas variáveis de estado, uma para limit e outra para offset. O useEffect que busca os dados de forma assíncrona sempre que as variáveis limit ou offset mudam.

Se você fez tudo corretamente, sua estrutura de dados ficará assim:

Estrutura de dados

e seu aplicativo ficará assim:

Tela

Não pareece tão ruim. E você pode ver que, embora haja 1.000 itens no array, apenas 10 estão sendo carregados. Que legal. Agora como faço para ver os outros itens? Ou seja, como eu mudo o estado de limit e offset?

Na paginação!

Vou criar uma pasta components, então dentro irei criar uma pasta Pagination com dois arquivos: index.tsx e styles.ts:

styles.ts:

import {
    FaCaretLeft,
    FaCaretRight,
    FaChevronLeft,
    FaChevronRight,
  } from "react-icons/fa";
  import styled from "styled-components";

  const Container = styled.div`
    width: 600px;
    display: grid;
    grid-template-columns: 1fr auto;
    font-size: 0.65rem;
    padding: 0.2rem;
  `;

  const factory = (Component: any = FaChevronLeft) => styled(Component)`
    cursor: pointer;
  `;

  const Left = factory(FaChevronLeft);

  const AllLeft = factory(FaCaretLeft);

  const Right = factory(FaChevronRight);

  const AllRight = factory(FaCaretRight);

  const PageContainer = styled.div`
    display: flex;
    align-items: center;
  `;

  const Page = factory(
    styled.div<{ isActive?: boolean }>`
      padding: 0.2rem;
      font-weight: ${({ isActive }) => isActive && "bold"};
    `
  );

  const PageInfo = styled.div`
    display: grid;
    grid-template-columns: auto auto 1fr;
    grid-gap: 0.4rem;
    align-items: center;
  `;

  export {
    Container,
    Left,
    AllLeft,
    PageContainer,
    Page,
    AllRight,
    Right,
    PageInfo,
  };
Enter fullscreen mode Exit fullscreen mode

E para index.tsx:

import {
    FaCaretLeft,
    FaCaretRight,
    FaChevronLeft,
    FaChevronRight,
  } from "react-icons/fa";
  import styled from "styled-components";

  const Container = styled.div`
    width: 600px;
    display: grid;
    grid-template-columns: 1fr auto;
    font-size: 0.65rem;
    padding: 0.2rem;
  `;

  const factory = (Component: any = FaChevronLeft) => styled(Component)`
    cursor: pointer;
  `;

  const Left = factory(FaChevronLeft);

  const AllLeft = factory(FaCaretLeft);

  const Right = factory(FaChevronRight);

  const AllRight = factory(FaCaretRight);

  const PageContainer = styled.div`
    display: flex;
    align-items: center;
  `;

  const Page = factory(
    styled.div<{ isActive?: boolean }>`
      padding: 0.2rem;
      font-weight: ${({ isActive }) => isActive && "bold"};
    `
  );

  const PageInfo = styled.div`
    display: grid;
    grid-template-columns: auto auto 1fr;
    grid-gap: 0.4rem;
    align-items: center;
  `;

  export {
    Container,
    Left,
    AllLeft,
    PageContainer,
    Page,
    AllRight,
    Right,
    PageInfo,
  };
Enter fullscreen mode Exit fullscreen mode

Sentry blog image

How to reduce TTFB

In the past few years in the web dev world, we’ve seen a significant push towards rendering our websites on the server. Doing so is better for SEO and performs better on low-powered devices, but one thing we had to sacrifice is TTFB.

In this article, we’ll see how we can identify what makes our TTFB high so we can fix it.

Read more

Top comments (0)

Billboard image

Create up to 10 Postgres Databases on Neon's free plan.

If you're starting a new project, Neon has got your databases covered. No credit cards. No trials. No getting in your way.

Try Neon for Free →

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay