Criando uma paginação do zero com React usando Hooks
Tags: react, javascript, tutorial, braziliandevs
Published: 2023-01-09 18:11:03

As aplicações que exibem grandes quantidades de dados na forma de uma lista ou tabela, podem implementar a paginação para dividir os dados em uma sequência de páginas. Em vez de buscar grandes quantidades de dados de uma API do servidor, a paginação carrega dados sob demanda, diminuindo o tempo de carregamento e melhorando a interface do usuário geral.

Embora existam muitas bibliotecas disponíveis para implementar a paginação, algumas são um pouco pesadas, prejudicando o desempenho geral da sua aplicação. Neste artigo, criaremos um componente de paginação reutilizável usando React Hooks.

Como exemplo, exibiremos a lista de passageiros na imagem abaixo, usando paginação.


![Imagem de paginação](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3990h4gnp7wc5mcqvg72.gif)

O que abordaremos neste artigo:

- Hook useSate
- Hook useEffect
- Configurando o projeto
- Criar um componente de paginação
- Usando o componente de paginação

Antes de entrar na implementação, deixe-me apresentar dois React Hooks que usaremos neste artigo.

## Hook useState

Se você quiser adicionar estado ao seu componente funcional, o Hook `UseState` fará o trabalho. Considere o exemplo abaixo:

```jsx
// uma nova variável de estado chamada "currentPage" 
const [currentPage, setCurrentPage] = useState(1);
```
O `useState` ajuda a preservar os valores entre as chamadas de função. Leva um único argumento, que é o estado inicial. No exemplo acima, a variável `currentPage` é definida como padrão `1`. O `useState` retornará um par de valores, sendo o primeiro o estado da variável e o segundo, a função para manipular o estado.

Usaremos o Hook `useState` para manter o estado da página atual, os limites mínimo e máximo da página, bem como manter os dados de resposta da API do passageiro.

## Hook useEffect

Se você quiser executar quaisquer efeitos colaterais, como busca de dados, manipulação de DOM ou inscrição e cancelamento de inscrição em eventos, o `useEffect` o ajudará a:

```jsx
useEffect (()=>{ // chama rest api },[]);

```

O React tem acesso ao estado do seu componente, portanto, ele basicamente lembrará a função que você passou e a invocará após as atualizações do DOM. Usamos o Hook `useEffect` para buscar dados de passageiros da API REST.

## Configurando o projeto

Nosso objetivo é exibir a lista de passageiros com paginação, então usaremos a API REST gratuita para fornecer uma lista de dados de passageiros. Comece criando uma nova aplicação React usando o comando Create React App abaixo:

```npx
npx create-react-app pagination-tutorial

```

Remova todo o conteúdo padrão do arquivo `App.js`. Incluiremos nossos componentes aqui mais tarde. O arquivo final deve se parecer com o código abaixo:

```jsx
import './App.css';
function App() {
  return (
    <div className="App">
       <div className="App-header">
           // inclua o componente Passenger, que criamos no final 
            do artigo

        </div>
    </div>
  );
}
export default App;
```
Agora, podemos executar a aplicação com o comando abaixo:

```jsx
yarn start
```

---

## Criando um componente de paginação

O componente funcional permite dividir a interface do usuário em partes independentes e reutilizáveis. Usaremos um componente funcional que possui um único argumento contendo props para o componente. Antes de criar nosso componente de paginação, vamos esclarecer alguns dos poucos termos usados no restante do artigo.

- Página atual: representa a posição do usuário na página
- Limite máximo de páginas: representa o limite superior da página
- Limite mínimo de página: representa o limite inferior da página

Vá para a pasta `src` e crie um novo arquivo chamado: `Pagination.js`


```jsx
import React from 'react';
const Pagination = (props)=> {
    return(
        <div>
        </div>
    )
}
export default Pagination;
```

Como estamos criando um componente reutilizável para paginação, suponha que o componente obtenha todos os dados do componente pai usando props. Nosso objetivo é criar uma saída como na imagem abaixo:


![Imagem de paginação](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g0vbvof75cgikq8rg0ay.png)

Inicialize os termos que discutimos na seção anterior:

```jsx
// init
  const { currentPage, maxPageLimit, minPageLimit} = props;
  const totalPages = props.response.totalPages-1;
  const data = props.response.data;
```

Agora, para inserir o número total de páginas, criaremos os números das páginas a partir de 1. O limite será baseado nos limites máximo e mínimo de páginas:


![Imagem de paginação](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/422vbs0qgqxre096fqwa.png)

Construa o array de páginas, que conterá todos os números desde 1 até o total de páginas:

```jsx
// construir um array com a quantidade de páginas com base no número total de páginas
    const pages = [];
    for (let i=1; i <= totalPages; i++) {
        pages.push(i);
    }
```

Para criar a interface do usuário com base no número de páginas que estão dentro dos limites mínimo e máximo de páginas, percorra a array de páginas construída com a etapa acima. Se a condição de interação corresponder á página atual, torne-a ativa:

```jsx
const pageNumbers = pages.map(page => {
        if(page <= maxPageLimit  && page > minPageLimit) {
            return(
        <li key={page} id={page} onClick={handlePageClick} 
            className={currentPage===page ? 'active' : null}>
            {page}
        </li>
            );
        } else {
            return null;
        }
    }

 );
```

Em seguida, criaremos reticências `...`, que aparecem quando há mais páginas do que a página atual á esquerda ou á direita. Ao clicar nas reticências, o usuário pode ir para o grupo de páginas anterior ou seguinte. Você pode vê-los na imagem abaixo, destacados em verde:


![Imagem de paginação verde](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hg5ce80h70bqz1oaaxc8.png)

```jsx
// page ellipses
    let pageIncrementEllipses = null;
    if(pages.length > maxPageLimit) {
        pageIncrementEllipses = <li onClick={handleNextClick}>&hellip;</li>
    }
    let pageDecremenEllipses = null;
    if(minPageLimit >=1){
        pageDecremenEllipses = <li onClick={handlePrevClick}>&hellip;</li> 
    }
const renderData = (data)=>{
```

Vamos renderizar nossos passageiros em um formato de lista. No código abaixo, estou mostrando o ID do passageiro e o nome da companhia aérea utilizada por cada passageiro:

```jsx
return(
        <ul>
            {data.map((d)=> 
            <li key={d['_id']}> The passenger having id {d['_id'].slice(d['_id'].length-5)} using {d.airline[0].name} airlines</li>)
            }
        </ul>
    )
}
```

Em seguida, combinaremos todos os elementos da interface do usuário e os retornaremos em nosso componente funcional, junto com os botões Anterior e Próximo.

Ao clicar em **Anterior**, o usuário navega para a página anterior. Ao clicar em **Próximo**, o usuário navega para a próxima página. Os elementos da interface do usuário são alinhados da seguinte maneira:

```jsx
Passenger’s data
2. Pager
    1. Prev button
    2. Page decrement ellipses
    3. Page numbers with current page selection
    4. Page increment ellipses
    5. Next button

 return (
        <div className="main">
            <div className="mainData">
              {renderData(data)}
            </div>
            <ul className="pageNumbers"> 
               <li>
                   <button onClick={handlePrevClick} disabled={currentPage === pages[0]}>Prev</button>
               </li>
               {pageDecremenEllipses}
                {pageNumbers}
               {pageIncrementEllipses}
                <li>
                   <button onClick={handleNextClick} disabled={currentPage === pages[pages.length-1]}&gt;Next</button>
               </li>
            </ul>
        </div>
    )
```

Até agora, criamos a IU, que inclui eventos como clique na página, clique anterior e próximo clique. Agora, vamos vincular todos os eventos:

```jsx
const handlePrevClick = ()=>{
        props.onPrevClick();
    }
    const handleNextClick = ()=>{
        props.onNextClick();
    }
    const handlePageClick = (e)=>{
        props.onPageChange(Number(e.target.id));
    }
```

## Usando o componente de paginação

Agora, vamos navegar até o componente pai, que é o componente `Passengers`, responsável por buscar os dados do passageiro e definir as props de paginação. Use o componente `Pagination` criado acima nesse componente.

Para criar o componente `Passengers`, primeiro inicialize o estado. Você pode consultar as variáveis de estado abaixo. Em termos simples, mostraremos o número de páginas começando do `1` ao `5`. Iniciamos os limites mínimo e máximo de páginas com `0` e `5`, respectivamente. 

Definimos a página atual como `1`, o que significa que o usuário estará na primeira página. Definindo os dados do passageiro como um array vazio, determinamos que buscaremos os dados nas próximas etapas. O limite de páginas, `5`, informa que mostraremos apenas cinco páginas na UI:

```jsx
const pageNumberLimit = 5;
  const [passengersData, setData] = useState([]);
  const [loading, setLoading] = useState(true);
  const [currentPage, setCurrentPage] = useState(1);
  const [maxPageLimit, setMaxPageLimit] = useState(5);
  const [minPageLimit, setMinPageLimit] = useState(0);
```

Quando a página atual mudar, buscaremos os dados do passageiro de nossa API com o Hook `useEffect`, mantendo-os na variável de estado `passagenrsData`:

```jsx
useEffect(()=>{
    setLoading(true);
    fetch(`https://api.instantwebtools.net/v1/passenger?currentPage=${currentPage}&size=5`)
    .then((response) => response.json())
    .then((json) => { setData(json); setLoading(false);});

  },[currentPage]);
```

A etapa principal dessa paginação completa é atualizar a página mínima, máxima e atual, que são atualizadas na mudança de página, no clique anterior e no próximo clique.

### Clique anterior

Suponha que você esteja na sexta página do nosso componente de paginação. Se você clicar no botão Anterior, será direcionado para a quinta página e os limites de página deverão ser redefinidos para 1 e 5.

Para obter o exemplo acima, atualizaremos os limites mínimo e máximo de página, somente se a página anterior não estiver no intervalo de páginas atual. O resto obtido após a divisão da página anterior pelo número de página, deve ser zero.


![Imagem de paginação](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lao1k8u61ddssjh6v9xl.png)

Por exemplo, considere que o limite máximo de páginas deve ser igual ao limite atual, menos o limite de número de páginas. No nosso exemplo na imagem acima, 10 menos cinco é igual a cinco.


![Imagem de paginação](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3tan23052fsiveobvw78.png)

Para o limite mínimo de páginas, subtraímos o limite do número de páginas, pelo limite mínimo atual. Em nosso exemplo, calculamos que seis menos cinco é igual a um. Lembre-se que a página atual, é a página anterior menos `1`.

### Clique próximo

Exploraremos o clique próximo, como uma alternância do exemplo anterior. Suponha que você esteja na quinta página agora. Clique em Próximo e você deve navegar para a sexta página. Os limites da página devem estar entre `6` e `10`.

Assim como no clique anterior, só podemos atingir o cenário acima atualizando o limite mínimo e máximo de páginas. Os limites mínimo e máximo de páginas, são atualizados apenas quando a próxima página é maior que o limite máximo de páginas atual:


![Imagem de paginação](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rzysazclrp8qwjwju6g1.png)

Para calcular o limite máximo de páginas, adicionamos o limite máximo atual ao limite de número de páginas. Em nosso exemplo, somamos cinco e cinco para igualar 10.

O limite mínimo de páginas é o limite mínimo atual mais o limite do número de páginas. No nosso exemplo, um mais cinco é igual a seis. A página atual é a página anterior mais uma.

### Mudança de página

Ao clicar em qualquer número de página, atualize a página atual para o número específico. Abaixo está o trecho de código para todos os principais eventos:

```jsx
const onPageChange= (pageNumber)=>{
    setCurrentPage(pageNumber);
  }
  const onPrevClick = ()=>{
      if((currentPage-1) % pageNumberLimit === 0){
          setMaxPageLimit(maxPageLimit - pageNumberLimit);
          setMinPageLimit(minPageLimit - pageNumberLimit);
      }
      setCurrentPage(prev=> prev-1);
   }

  const onNextClick = ()=>{
       if(currentPage+1 > maxPageLimit){
           setMaxPageLimit(maxPageLimit + pageNumberLimit);
           setMinPageLimit(minPageLimit + pageNumberLimit);
       }
       setCurrentPage(prev=>prev+1);
    }
```

Para criar a UI para passageiros com paginação, vamos combinar todos os atributos necessários e enviá-los como props para o componente de paginação:

```jsx
const paginationAttributes = {
    currentPage,
    maxPageLimit,
    minPageLimit,
    response: passengersData,
  };
  return(
    <div>
        <h2>Passenger List</h2>
        {!loading ? <Pagination {...paginationAttributes} 
                          onPrevClick={onPrevClick} 
                          onNextClick={onNextClick}
                          onPageChange={onPageChange}/>
        : <div> Loading... </div>
        }
    </div>
)

```

Agora, construímos os componentes pai e filho. Você está pronto para usar o componente pai em qualquer lugar da aplicação.

## Conclusão

Neste tutorial, criamos um componente de paginação enviando a página atual, limite máximo de página, limite mínimo de página e outros eventos de página como props para o componente. Usamos React Hooks para gerenciamento de estado e busca de dados.

Enquanto desenvolvemos nossa aplicação de exemplo em React, você pode aplicar a mesma lógica a qualquer projeto. Espero que você tenha aprendido algo novo! Certifique-se de deixar um comentário se tiver alguma dúvida.