Scroll Infinito no React-Native - Como adicionar ao seu aplicativo e suas vantagens

Scroll Infinito no React-Native - Como adicionar ao seu aplicativo e suas vantagens

Eduardo Gonçalves Souza

Escrito por: Eduardo Gonçalves Souza

Publicado em: 15/12/2024

Última atualização: 15/12/2024

Você já se perguntou como alguns aplicativos que usamos frequentemente no nosso dia a dia conseguem carregar conteúdo sem fim no seu “feed”? Esse recurso se chama “Scroll Infinito”, esse recurso é muito comum em aplicativos mobiles, esse recurso consiste em carregar novos dados conforme o usuário desce/rola a tela para baixo.

O que é Scroll Infinito?

O scroll infinito é uma técnica normalmente usada para o design de interface do usuário que carrega mais conteúdo automaticamente à medida que o usuário rola para o final do feed. Em vez de colocar o conteúdo em páginas ou forçar o usuário a clicar em um botão "Carregar mais" ou “Mostrar mais”, o scroll infinito cria uma experiência fluida, contínua e até intuitiva.

Implementando Scroll Infinito

Vamos criar um exemplo prático!

Utilizaremos o “React-Native” com “TypeScript” e a “API do Github” (https://api.github.com), vamos buscar tópicos com a palavra React, com 15 itens por página, e começar na página 1, a URL modificada vai ficar assim:

https://api.github.com/search/topics?q=react&per_page=15&page=1

Passo 1: Configuração Inicial

Primeiro, vamos configurar nossos componentes, states e tipos:

import { useState, useEffect } from "react";
import {
  View,
  Text,
  FlatList,
  StyleSheet,
  ActivityIndicator
} from "react-native";

type Topic = {
  name: string;
  short_description: string;
};

export default function App() {
  const [data, setData] = useState<Topic[]>([]);
  const [page, setPage] = useState(1);
  const [loading, setLoading] = useState(false);
}

Passo 2: Renderizando dados temporários.

Agora vamos fazer a renderização dos elementos com a FlatList, adicionar alguns dados temporários para dar uma olhada no que temos até o momento:

 // ... (Código anterior)

export default function App() {
  const [data, setData] = useState<Topic[]>([
    {
      name: "react",
      short_description:
        "A declarative, efficient, and flexible JavaScript library for building user interfaces.",
    },
    {
      name: "redux",
      short_description: "A Predictable State Container for JS Apps.",
    },
    {
      name: "typescript",
      short_description:
        "A typed superset of JavaScript that compiles to plain JavaScript.",
    },
    {
      name: "jest",
      short_description:
        "A delightful JavaScript Testing Framework with a focus on simplicity.",
    },
    {
      name: "webpack",
      short_description:
        "A static module bundler for modern JavaScript applications.",
    },
  ]);

  const [page, setPage] = useState(1);
  const [loading, setLoading] = useState(false);

  const renderItem = ({ item }: { item: Topic }) => (
    <View style={styles.listItem}>
      <Text style={styles.topicName}>{item.name}</Text>
      <Text style={styles.topicDescription}>{item.short_description}</Text>
    </View>
  );

  return (
    <FlatList
      contentContainerStyle={{ marginTop: 30, marginHorizontal: 20 }}
      data={data}
      renderItem={renderItem}
      keyExtractor={(item) => item.name}
    />
  );

const styles = StyleSheet.create({
  list: {
    paddingHorizontal: 20,
  },
  listItem: {
    backgroundColor: "#f0f0f0",
    marginTop: 20,
    padding: 20,
    borderRadius: 10,
  },
  topicName: {
    fontSize: 18,
    fontWeight: "bold",
    marginBottom: 5,
  },
  topicDescription: {
    fontSize: 14,
    color: "#666",
  },
});

O resultado deve ser mais ou menos assim:

Exemplo estático de uma lista de dados com nome e descrição

Passo 3: Puxando dados da API

Agora que criamos uma interface básica, vamos buscar os dados da API para preencher a lista com os primeiros 15 itens.

Para centralizar algumas informações importantes, definiremos 3 variáveis abaixo dos nossos States:

const baseURL = "https://api.github.com";
const searchTerm = "react";
const itensPerPage = 15;

Com esses dados definidos, vamos criar uma função chamada loadTopicsFromAPI para buscar os dados da API e colocar os dados nos seus States:

// ... (Código anterior)

const loadTopicsFromAPI = async () => {
  if (loading) return;

  setLoading(true);

  const response = await fetch(
    `${baseURL}/search/topics?q=${searchTerm}&per_page=${itensPerPage}&page=${page}`
  );
  const result = await response.json();

  setData((prevData) => [...prevData, ...result.items]);
  setPage((prevPage) => prevPage + 1);

  setLoading(false);
};

// ... (Continuação do código)

🤔 Dica: Não sabe usar o fetch? Veja um tutorial nesse link: "https://codigoaoponto.com/blog/como-consumir-uma-api-no-javascript-com-a-fetch-api"

Com isso, temos uma função para puxar os dados da API, então não precisamos mais dos dados temporários, e também usaremos o useEffect para chamar a função toda vez que a página é carregada:

export default function App() {
  const [data, setData] = useState<Topic[]>([]);
  const [page, setPage] = useState(1);
  const [loading, setLoading] = useState(false);

  const baseURL = "https://api.github.com";
  const searchTerm = "react";
  const itensPerPage = 15;

  useEffect(() => {
    loadTopicsFromAPI();
  }, []);

  const loadTopicsFromAPI = async () => {
    if (loading) return;

    setLoading(true);

    const response = await fetch(
      `${baseURL}/search/topics?q=${searchTerm}&per_page=${itensPerPage}&page=${page}`
    );
    const result = await response.json();

    setData((prevData) => [...prevData, ...result.items]);
    setPage((prevPage) => prevPage + 1);

    setLoading(false);
  };

    // ... (Continuação do código)

Sua aplicação deve estar desse jeito:

Exemplo de uma lista de dados puxados de uma API com nome e descrição

Passo 4: Adicionando o Scroll Infinito

Vamos adicionar agora os métodos que darão funcionamento ao scroll infinito, a função loadTopicsFromAPI, além de carregar os primeiros dados, também adiciona mais 1 página sempre que chamada, fazendo carregar ainda mais dados!

Vamos até a FlatList, e adicionaremos as propriedades onEndReached e onEndReachedThreshold :

// ... (Código anterior)

return (
  <FlatList
    contentContainerStyle={{ marginTop: 30, marginHorizontal: 20 }}
    data={data}
    renderItem={renderItem}
    keyExtractor={(item) => item.name}
    onEndReached={loadTopicsFromAPI}
    onEndReachedThreshold={0.1}
  />
);

// ... (Continuação do código)
  • 💡 Explicação:
    • onEndReached: Chama uma função toda vez que a lista chega ao final!
    • onEndReachedThreshold: Define basicamente o percentual de distância do fim da lista para carregar novos dados, 0,1 significa 10% e 1 significa 100%.

Se tudo funcionou como esperado, você deve estar vendo este resultado:

Exemplo de uma lista de dados puxados de uma API com nome e descrição e com scroll infinito

Ótimo, o scroll infinito está funcionando! Agora, para aprimorar a experiência do usuário e deixar mais intuitivo, vamos adicionar um indicador de carregamento ao final da lista quando estivermos carregando novos itens:

<FlatList
 ...
 ListFooterComponent={renderFooter}
/>

E vamos criar um componente acima do componente RenderItem e utilizar o ActivityIndicator do React-Native :

// ... (Código anterior)

const renderFooter = () => {
  if (!loading) return null;

  return (
    <View style={{ alignSelf: "center", marginVertical: 20 }}>
      <ActivityIndicator size="large" color="#0000ff" />
    </View>
  );
};

// ... (Continuação do código)

Juntando tudo que construímos até agora, temos algo assim como resultado final:

Exemplo de uma lista de dados puxados de uma API com nome e descrição e com scroll infinito e uma bolinha de carregamento

Resumindo!

Implementar um scroll infinito se encaixa em muitos projetos e melhora a experiência do usuário da aplicação, e nós vemos isso no nosso dia a dia, afinal os principais aplicativos como YouTube, Instagram, TikToc, entre outros.

Algumas vantagens.

  1. Como dito antes, melhora a experiência do usuário:
    • Proporciona uma navegação fluida e ininterrupta.
    • Elimina a necessidade de clicar em botões de "próxima página" ou "carregar mais".
    • Cria uma sensação de conteúdo infinito, incentivando o engajamento do usuário.
  2. Otimização de Desempenho:
    • Carrega dados sob demanda, reduzindo o uso de memória do dispositivo.
    • Melhora o tempo de carregamento inicial do aplicativo.
    • Permite lidar eficientemente com grandes conjuntos de dados.
  3. Economia de Recursos:
    • Reduz o uso de dados do usuário, carregando conteúdo apenas quando necessário.
    • Diminui a carga no servidor, pois os dados são solicitados em lotes menores.
  4. Adaptabilidade:
    • Funciona bem em diferentes tamanhos de tela e orientações de dispositivo.
  5. Aumento do Tempo de Sessão.
    • Encoraja os usuários a continuarem rolando e consumindo mais conteúdo.
    • Pode levar a maior engajamento e retenção de usuários.

Agora é com você! Experimente implementar o scroll infinito em seus projetos React Native e veja a diferença que faz!