Criando uma tabela de rolagem infinita sem ouvinte de evento

Por que fica assim?



Se, ao renderizar uma tabela enorme com algum tipo de animação de transição, nada mais for feito, o aplicativo ficará lento e o usuário sofrerá.




Na maioria dos casos, quando usamos o ouvinte de eventos para criar uma tabela com rolagem infinita, precisamos não apenas executar cálculos relacionados à viewport e altura da linha, mas também escrever muita lógica no manipulador de rolagem para evitar redesenho com muita frequência, pois cada rolagem de retorno de chamada será chame setState .


O código seria algo como isto:


 componentDidMount() { window.addEventListener('scroll', this.handleScroll) } handleScroll(e) { // use window offset and boundingRect const { ...someAttributes } = window; const { ...someBoundingRect } = this.component // some logic prevent re-render if ( ... ) return; // do some math const newIndex = ... // and how many rows should be rendered this.setState({index: newIndex }) } 

Mas há outra abordagem para implementar a rolagem infinita de uma tabela, sem saber nada sobre os valores window ou boundingRect.


Este é um IntersectionObserver . Definição de w3c :


Esta especificação descreve uma API que pode ser usada para descobrir a visibilidade e a posição dos elementos DOM ("destinos") em relação aos elementos contidos neles.

Ao usar esse método, você não precisa saber a altura da linha, a parte superior da janela de exibição ou qualquer outro valor para fazer as contas.



O conceito é organizar âncoras com índices em cada ponto de controle e cada vez que a âncora é acionada, obtenha o valor do índice e redesenhe a tabela. Graças a isso, você não precisa fazer nenhuma mágica matemática com uma altura e janela de visualização DOM.



Gatilho de âncora para o índice 1


Renderizar mais linhas


O código com IntersectionObserver será algo como isto.



 handleSentinel = (c) => { if(!this.observer) { //  observer this.observer = new IntersectionObserver( entries => { entries.forEach(e => { //   ,    if (e.isIntersecting) { this.setState( { cursor: +e.target.getAttribute('index') } ); } }); }, { root: document.querySelector('App'), rootMargin: '-30px', } } if (!c) return; //    this.observer.observe(c) } render() { const blockNum = 5; return( ... <tbody> {MOCK_DATA.slice(0, (cursor+1) * blockNum).map(d => <Block> { //       // ,   5  d.id % blockNum === 0 ? <span ref={this.handleSentinel} index={d.id / blockNum} /> : null } </Block>)} </tbody> ... ) } 

Source: https://habr.com/ru/post/pt456046/


All Articles