Esta é uma mistura de uma mesa infinita e um cenário de rolagem infinita. A melhor abstração que encontrei para isso é a seguinte:
Visão geral
Faça um <List>
componente que tenha um array de todos os filhos. Como não os renderizamos, é muito barato apenas alocá-los e descartá-los. Se as alocações de 10k forem muito grandes, você poderá passar uma função que tenha um intervalo e retornar os elementos.
<List>
{thousandelements.map(function() { return <Element /> })}
</List>
Seu List
componente está monitorando qual é a posição de rolagem e renderiza apenas os filhos que estão em exibição. Ele adiciona um grande div vazio no início para simular os itens anteriores que não são renderizados.
Agora, a parte interessante é que, uma vez que um Element
componente é renderizado, você mede sua altura e o armazena em seu List
. Isso permite que você calcule a altura do espaçador e saiba quantos elementos devem ser exibidos na visualização.
Imagem
Você está dizendo que quando a imagem está carregando eles fazem tudo "saltar" para baixo. A solução para isso é definir as dimensões da imagem em sua tag img:<img src="..." width="100" height="58" />
. Desta forma, o navegador não precisa esperar para baixá-lo para saber o tamanho que será exibido. Isso requer alguma infraestrutura, mas realmente vale a pena.
Se você não pode saber o tamanho com antecedência, adicione onload
ouvintes à sua imagem e, quando ela for carregada, meça sua dimensão exibida e atualize a altura da linha armazenada e compense a posição de rolagem.
Pulando em um elemento aleatório
Se você precisa pular para um elemento aleatório na lista, isso vai exigir alguns truques com a posição de rolagem, porque você não sabe o tamanho dos elementos intermediários. O que eu sugiro que você faça é calcular a média das alturas dos elementos que você já calculou e pular para a posição de rolagem da última altura conhecida + (número de elementos * média).
Como isso não é exato, causará problemas quando você chegar à última posição válida conhecida. Quando ocorre um conflito, basta alterar a posição do scroll para corrigi-lo. Isso vai mover a barra de rolagem um pouco, mas não deve afetá-lo muito.
React Specifics
Você deseja fornecer uma chave para todos os elementos renderizados para que sejam mantidos entre os renderizadores. Existem duas estratégias: (1) ter apenas n chaves (0, 1, 2, ... n) onde n é o número máximo de elementos que você pode exibir e usar seu módulo de posição n. (2) tem uma chave diferente por elemento. Se todos os elementos compartilham uma estrutura semelhante, é bom usar (1) para reutilizar seus nós DOM. Se não o fizerem, use (2).
Eu teria apenas duas partes do estado React: o índice do primeiro elemento e o número de elementos sendo exibidos. A posição de rolagem atual e a altura de todos os elementos seriam diretamente anexadas this
. Ao usar, setState
você está, na verdade, fazendo um rerender que só deve acontecer quando o intervalo muda.
Aqui está um exemplo de lista infinita usando algumas das técnicas que descrevo nesta resposta. Vai dar certo, mas o React é definitivamente uma boa maneira de implementar uma lista infinita :)