Logo

dev-resources.site

for different kinds of informations.

Aplicando paginação em um componente Select

Published at
4/9/2024
Categories
react
component
webdev
development
Author
kyyaramero
Author
10 person written this
kyyaramero
open
Aplicando paginação em um componente Select

Sabe aquele momento que você possui dados vindo com paginação de alguma API? Por exemplo, uma lista de frutas disponíveis em um mercado que são chamados pelo client da aplicação e recebidos como um array de objetos?

Bem, acredito que todo desenvolvedor front-end já deve ter passado por isso algumas vezes e não costuma ser algo difícil de lidar. Até que seja uma quantidade enorme de dados e esses devem ser listados de forma paginada também na interface, por questão de performance e usabilidade.

Caso sejam listados numa página, num componente de exibição, basta aplicar um loading infinito ou alguma lógica semelhante. No entanto, caso seja necessário adicionar esses dados em um formulário, em um select, especificamente, o fácil se torna um tanto quanto mais complicado.

Foi enfrentando esse desafio que encontrei uma forma para exibir uma lista dentro de um select de forma em que a página, a requisição buscando mais dados, seja chamada sempre que a barra de rolagem do mouse chega ao fim da lista atual. Para isso, utilizei a biblioteca react-select-async-paginate

Ela disponibiliza de um componente muito similar aquele presente na biblioteca react-select porém, com um comportamento adicional, recebido dentro do parâmetro loadOptions.

 <AsyncPaginate
      additional={defaultAdditional}
      value={value}
      loadOptions={loadPageOptions}
      styles={{
        control: base => controlStyles(base, '36px', '300px'),
        menuList: menuListStyles,
      }}
      loadingMessage={() => 'Carregando...'}
      onChange={(selectedOption: OptionType | null) => {
        onChange(selectedOption)
      }}
      placeholder="Selecione uma fruta"
    />
Enter fullscreen mode Exit fullscreen mode

Dicionário de componente:

  • Nome: AsyncPaginate
  • Parâmetros:
    • additional: onde a paginação se inicia.
    • value: valor do select.
    • loadOptions: recebe função que controlará a paginação (ou outra lógica que queira adicionar para buscar os dados).
    • styles: estilos do componente.
    • loadingMessage: mensagem a ser exibida enquanto as opções são carregadas.
    • onChange: recebe função que controlará qualquer mudança quanto a opção selecionada.
    • placeholder: texto padrão para quando não houver opção selecionada ou mudança realizada.

O mais crucial a ser levado em conta aqui é a função passada para loadOptions, pois é quem fará a gestão do nosso componente. Aqui, se chama loadPageOptions e possui a seguinte estrutura:

 const loadPageOptions = async (query: string) => {
    const { options, hasMore } = await searchOptions(query, pageRef.current)
    const nextPage = pageRef.current + 1

    pageRef.current = nextPage

    return {
      options,
      hasMore,

      additional: {
        page: nextPage,
      },
    }
  }
Enter fullscreen mode Exit fullscreen mode

É uma função assíncrona esperando dados da API e controlando a variáveis hasMore (responsável por armazenar a possibilidade de existir mais páginas para serem buscadas e options (opções a serem exibidas no select). Além disso, additional é um objeto incrementando o valor atual de pageRef para apontar para a próxima página.

Quem cuida dos fatores mais específicos como chamada a API e estruturação das opções (já que o select espera uma lista de objetos contendo value e label) é searchOptions:

Obs. Por questões didáticas, há uma falsa API de frutas, simulando uma chamada retornando número total de páginas e dez itens por página.

// Importa os dados de frutas, o tamanho da página e o número total de páginas do arquivo 'data'
import { frutas, pageSize, totalPages } from './data'
// Importa o tipo OptionType
import { OptionType } from './type'

// Função que simula um atraso de tempo
const sleep = (ms: number) =>
  new Promise(resolve => {
    setTimeout(() => {
      resolve(undefined)
    }, ms)
  })

// Função assíncrona que carrega as opções de frutas com base na pesquisa e no número da página
export const searchOptions = async (search: string, page: number) => {
  // Aguarda 500 milissegundos (0.5 segundos) para simular um carregamento assíncrono
  await sleep(500)

  // Calcula os índices de início e fim da página atual
  const startIndex = (page - 1) * pageSize
  const endIndex = startIndex + pageSize
  // Seleciona os itens da página atual
  const pageFrutas = frutas.slice(startIndex, endIndex)

  // Mapeia os itens para o formato de OptionType
  const options: OptionType[] = pageFrutas.map(item => ({
    value: item.index,
    label: item.nome,
  }))
  let filteredOptions: OptionType[]

  // Filtra as opções com base na pesquisa, ignorando maiúsculas e minúsculas
  if (!search) {
    filteredOptions = options
  } else {
    const searchLower = search.toLowerCase()
    filteredOptions = options.filter(({ label }) =>
      label.toLowerCase().includes(searchLower)
    )
  }

  // Determina se há mais páginas a serem carregadas
  const hasMore = totalPages > page
  return {
    options: filteredOptions,
    hasMore,
  }
}

Enter fullscreen mode Exit fullscreen mode

O comportamento resultante do componente é este. Como uma base para customização, implementação de multiseleção e diversas outras capacidades, tem potencial para ser aplicada em diversos cenários, onde tal tipo de carregamento seja necessário ao usuário.

react_select_loading
O código completo está disponível no repositório do Github.

component Article's
30 articles in total
Favicon
Build a note app with JavaScript component.
Favicon
Key characteristic of Component-Based Architecture
Favicon
React Component Libraries: Overview of 19 Top Libs
Favicon
Styling Components in React 🧢
Favicon
Building a Seamless OTP Input Field in React: A Step-by-Step Guide
Favicon
MithrilJS component with state management
Favicon
[Off Topic] Nano introdução do framework Angular para Devs do back
Favicon
Comparing Vue Component Documentation tools
Favicon
Laravel 11 Custom Component File Structure
Favicon
Building Message Component in Vue3
Favicon
Aplicando paginação em um componente Select
Favicon
How much does it cost to repair an outdoor LED display?
Favicon
Global toast in Vue3
Favicon
Unveiling the Hidden Gem of React: The Power of Compound Components
Favicon
Controlled vs Uncontrolled Components in React
Favicon
React components -(Class component v/s function component)
Favicon
3 Ways to Create React Components with Bit
Favicon
Client or Server component ?
Favicon
Desenvolvimento de Componentes Assíncronos com Vue.js
Favicon
NAND Flash vs NOR Flash: Differences between them
Favicon
Link Component in React Router
Favicon
Guia de Components - para quem tem pressa!
Favicon
How to exchange data between Angular components
Favicon
Component Testing with Cypress and Reactjs
Favicon
React - Higher Order Components (HOC)
Favicon
How to Create Component Library Like Material UI or Mantine UI?
Favicon
Looking forward to adding Algolia's DOCSEARCH to Mantine DataTable
Favicon
Cypress Component Testing vs React Testing Library - the complete comparison
Favicon
Creating Custom Component for NPS Feedback
Favicon
Maximize your Angular code reusability using <NgTemplateOutlet>

Featured ones: