Scroll infinito no React Native

Em muitos casos precisamos tomar cuidado com o carregamento de muitos componentes em listas no React Native. No exemplo desse post iremos utilizar a API de busca de repositórios do Github que nos permite paginar sobre os resultados.

Exemplo de chamada à API buscando pela palavra react com 20 itens por página e na página 5: https://api.github.com/search/repositories?q=react&page=5&per_page=20

Bora ver como isso fica na prática!

Configurando FlatList

Primeiramente vamos configurar nosso componente para renderizar uma FlatList com alguns dados de exemplo armazenados no estado:

import React, { Component } from 'react';

import { View, Text, FlatList, StyleSheet } from 'react-native';

export default class App extends Component {
  state = {
    data: [
      { id: 0, full_name: 'Repo 1' },
      { id: 1, full_name: 'Repo 2' },
      { id: 2, full_name: 'Repo 3' },
      { id: 3, full_name: 'Repo 4' },
      { id: 4, full_name: 'Repo 5' },
    ],
  };

  renderItem = ({ item }) => (
    <View style={styles.listItem}>
      <Text>{item.full_name}</Text>
    </View>
  );

  render() {
    return (
      <FlatList
        style={{ marginTop: 30 }}
        contentContainerStyle={styles.list}
        data={this.state.data}
        renderItem={this.renderItem}
        keyExtractor={item => item.id}
      />
    );
  }
}

const styles = StyleSheet.create({
  list: {
    paddingHorizontal: 20,
  },

  listItem: {
    backgroundColor: '#EEE',
    marginTop: 20,
    padding: 30,
  },
});

O resultado do código deve ser assim:

Agora com a interface pré-configurada vamos buscar os dados da API do Github para preencher a lista com os primeiros 20 itens.

Vou começar definindo 3 variáveis acima de nossa classe apenas para centralizar as informações importantes:

const baseURL = 'https://api.github.com';
const searchTerm = 'react';
const perPage = 20;

Agora, vamos criar uma função chamada loadRepositories e invocá-la no método componentDidMount para preencher nosso estado, além disso, alterei meu estado para armazenar o valor da página atual e da informação de carregamento de novos resultados:

export default class App extends Component {
  state = {
    data: [],
    page: 1,
    loading: false,
  };

  componentDidMount() {
    this.loadRepositories();
  }

  loadRepositories = async () => {
    if (this.state.loading) return;

    const { page } = this.state;

    this.setState({ loading: true });

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

    this.setState({
      data: [ ...this.state.data, ...repositories.items ],
      page: page + 1,
      loading: false,
    });
  }
  
  // Resto da classe
}

O método loadRepositories é utilizado para o carregamento inicial da nossa lista, mas devido à linha 23 que realiza a concatenação dos valores recebidos da API ao estado e à linha 24 que aumenta o número da página atual, podemos utilizar esse método também para carregamento dos novos itens quando chegamos ao fim da lista.

Até agora sua aplicação deve estar assim:

Vamos adicionar agora os métodos que darão funcionamento ao scroll infinito na nossa FlatList:

<FlatList
  ...
  onEndReached={this.loadRepositories}
  onEndReachedThreshold={0.1}
/>

A propriedade onEndReachedThreshold define basicamente o percentual de distância do fim que o usuário deve chegar para carregar novos dados, nesse caso, 0.1 significa 10%.

Se tudo ocorreu bem até agora, sua aplicação deve estar tendo esse comportamento:

Legal, nosso scroll infinito já está funcionando, apenas para melhorar a interface vamos adicionar um loading no fim da lista sempre que estivermos realizando o carregamento de novos itens:

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

E podemos importar o componente ActivityIndicator do React Native e criar um método renderFooter em nossa classe:

renderFooter = () => {
  if (!this.state.loading) return null;
  return (
    <View style={styles.loading}>
      <ActivityIndicator />
    </View>
  );
};

O resultado final deve ser algo como:

Concluindo

Esse post não possui scroll infinito, então acaba por aqui…

A utilização do scroll infinito se aplica muito bem a diversos tipos de projetos, veja que Instagram, Facebook, AirBnB e vários outros aplicativos que possuem um feed principal de alguma forma utilizam essa técnica para evitar processamento desnecessário.

Aplicar as técnicas de scroll infinito com Lazy Load e uma boa interface de pré-carregamento devem dar à sua aplicação um ar de acessibilidade bem mais limpa 🙂

Código do post: https://github.com/Rocketseat/blog-scroll-infinito-react-native

Se gostou do post, não esquece de deixar  seu comentário!