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
ou
Usando yarn:
yarn create react-app pagination --template typescript
Acesse o diretório do projeto e abra-o com o seu editor favorito:
cd pagination
code .
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
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;
}
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[];
}
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,
}
}
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 };
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;
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:
e seu aplicativo ficará assim:
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,
};
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,
};
Top comments (0)