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 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.
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:
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);
}
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:
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:
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)
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:
Ó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:
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.
Agora é com você! Experimente implementar o scroll infinito em seus projetos React Native e veja a diferença que faz!