Guia completo para rolagem infinita em React
  Rolagem infinita é um padrão de UX onde novo conteúdo é carregado conforme os usuários rolam a página para baixo. Comum em feeds sociais, linhas do tempo e aplicativos de notícias, é uma maneira fluida de explorar grandes conjuntos de dados sem usar botões de paginação. Neste guia, você aprenderá como construir rolagem infinita em React usando tanto uma biblioteca de terceiros quanto um hook personalizado.
Principais Aprendizados
- Aprenda 2 maneiras práticas de implementar rolagem infinita em React
 - Use um hook personalizado com IntersectionObserver ou um pacote de terceiros
 - Gerencie estados de carregamento, paginação e resultados vazios
 
O que é rolagem infinita?
Rolagem infinita carrega novos dados conforme o usuário rola próximo ao final de uma lista. Ela elimina a necessidade de paginação e cria uma experiência de navegação contínua. Você verá isso em produtos como Instagram, Twitter e Reddit.
Método 1: Usando um pacote (react-infinite-scroll-component)
A maneira mais fácil de implementar rolagem infinita em React é com um pacote bem testado.
Passo 1: Instalação
npm install react-infinite-scroll-component
Passo 2: Exemplo de uso básico
import InfiniteScroll from 'react-infinite-scroll-component';
function Feed() {
  const [items, setItems] = useState([]);
  const [page, setPage] = useState(1);
  const [hasMore, setHasMore] = useState(true);
  const fetchMoreData = async () => {
    const res = await fetch(`/api/posts?page=${page}`);
    const newItems = await res.json();
    if (newItems.length === 0) setHasMore(false);
    else {
      setItems(prev => [...prev, ...newItems]);
      setPage(prev => prev + 1);
    }
  };
  return (
    <InfiniteScroll
      dataLength={items.length}
      next={fetchMoreData}
      hasMore={hasMore}
      loader={<h4>Loading...</h4>}
      endMessage={<p>No more results</p>}
    >
      {items.map(item => <div key={item.id}>{item.title}</div>)}
    </InfiniteScroll>
  );
}
Prós
- Rápido de configurar
 - Estados de carregamento e finalização integrados
 
Contras
- Menos controle sobre a lógica de rolagem
 - Adiciona dependência de pacote
 
Método 2: Hook personalizado com IntersectionObserver
Este método oferece controle total e sem dependências extras.
Passo 1: Criar um hook
function useInfiniteScroll(callback, ref) {
  useEffect(() => {
    const observer = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) callback();
    });
    if (ref.current) observer.observe(ref.current);
    return () => observer.disconnect();
  }, [callback, ref]);
}
Passo 2: Usar em um componente
function Feed() {
  const [items, setItems] = useState([]);
  const [page, setPage] = useState(1);
  const [hasMore, setHasMore] = useState(true);
  const sentinelRef = useRef(null);
  const loadMore = async () => {
    if (!hasMore) return;
    const res = await fetch(`/api/posts?page=${page}`);
    const newItems = await res.json();
    if (newItems.length === 0) setHasMore(false);
    else {
      setItems(prev => [...prev, ...newItems]);
      setPage(prev => prev + 1);
    }
  };
  useInfiniteScroll(loadMore, sentinelRef);
  return (
    <div>
      {items.map(item => <div key={item.id}>{item.title}</div>)}
      <div ref={sentinelRef} style={{ height: 1 }} />
    </div>
  );
}
Prós
- Totalmente personalizável
 - Funciona com qualquer lógica de rolagem ou API
 
Contras
- Requer mais configuração
 - IntersectionObserver não é suportado no IE11
 
Padrões e dicas do mundo real
- Aplicar debounce nas chamadas de fetch para evitar sobrecarregar as APIs
 - Mostrar um spinner ou loader esqueleto para melhor UX
 - Adicionar lógica de nova tentativa para erros de rede
 - Lidar com casos extremos como zero resultados ou páginas vazias
 
if (items.length === 0 && !hasMore) {
  return <p>No posts found.</p>;
}
Considerações de desempenho
- Use bibliotecas como 
react-windowpara virtualização ao renderizar milhares de itens - Memorize componentes de itens para evitar re-renderizações
 - Limpe os observadores adequadamente para evitar vazamentos de memória
 
useEffect(() => {
  const observer = new IntersectionObserver(...);
  return () => observer.disconnect();
}, []);
Conclusão
Rolagem infinita é um recurso comum em interfaces modernas. Seja você preferindo a simplicidade de um pacote ou o controle de um hook personalizado, o React torna ambas as abordagens acessíveis. Sempre considere o desempenho e o feedback de carregamento ao implementar rolagem infinita.
Perguntas Frequentes
Usar uma biblioteca como `react-infinite-scroll-component` oferece uma configuração rápida e conveniente.
Defina uma flag `hasMore` como false quando a API retornar um array vazio.
Pode prejudicar, se você estiver renderizando muitos itens. Use ferramentas de virtualização como `react-window` para gerenciar listas grandes.
Use `IntersectionObserver` para acionar o carregamento de dados quando uma `div` inferior (sentinela) entrar no campo de visão.