Nieskończone przewijanie dzięki React JS


89

Szukam sposobów implementacji nieskończonego przewijania w React. Natknąłem się na respons-infinite-scroll i stwierdziłem, że jest to nieefektywne, ponieważ po prostu dodaje węzły do ​​DOM i nie usuwa ich. Czy istnieje sprawdzone rozwiązanie z Reactem, które doda, usunie i utrzyma stałą liczbę węzłów w DOM?

Oto problem z jsfiddle . W tym problemie chcę mieć jednocześnie tylko 50 elementów w DOM. inne powinny być ładowane i usuwane, gdy użytkownik przewija w górę iw dół. Zaczęliśmy używać Reacta ze względu na jego algorytmy optymalizacyjne. Teraz nie mogłem znaleźć rozwiązania tego problemu. Natknąłem się na Airbnb Infinite JS . Ale jest zaimplementowany w Jquery. Aby skorzystać z tego nieskończonego zwoju airbnb, muszę stracić optymalizację React, której nie chcę robić.

przykładowy kod, który chcę dodać, to scroll (tutaj ładuję wszystkie elementy. Moim celem jest załadowanie tylko 50 elementów na raz)

/** @jsx React.DOM */

var Hello = React.createClass({
    render: function() {
        return (<li>Hello {this.props.name}</li>);
    }
});

var HelloList = React.createClass({ 
     getInitialState: function() {                            
         var numbers =  [];
         for(var i=1;i<10000;i++){
             numbers.push(i);
         }
         return {data:numbers};
     },

    render: function(){
       var response =  this.state.data.map(function(contact){          
          return (<Hello name="World"></Hello>);
        });

        return (<ul>{response}</ul>)
    }
});

React.renderComponent(<HelloList/>, document.getElementById('content'));

Szukam pomocy ...

Odpowiedzi:


57

Zasadniczo podczas przewijania chcesz zdecydować, które elementy są widoczne, a następnie ponownie wyświetlić tylko te elementy, z pojedynczym elementem odstępnika na górze i na dole, aby reprezentować elementy poza ekranem.

Vjeux zrobił tutaj skrzypce, na które możesz spojrzeć: jsfiddle .

Po przewinięciu wykonuje

scrollState: function(scroll) {
    var visibleStart = Math.floor(scroll / this.state.recordHeight);
    var visibleEnd = Math.min(visibleStart + this.state.recordsPerBody, this.state.total - 1);

    var displayStart = Math.max(0, Math.floor(scroll / this.state.recordHeight) - this.state.recordsPerBody * 1.5);
    var displayEnd = Math.min(displayStart + 4 * this.state.recordsPerBody, this.state.total - 1);

    this.setState({
        visibleStart: visibleStart,
        visibleEnd: visibleEnd,
        displayStart: displayStart,
        displayEnd: displayEnd,
        scroll: scroll
    });
},

a wtedy funkcja renderowania wyświetli tylko wiersze z zakresu displayStart..displayEnd.

Możesz być także zainteresowany ReactJS: Modeling Bi-Directional Infinite Scrolling .


2
To świetna technika ... dzięki! Jednak kończy się niepowodzeniem, gdy parametr recordHeight jest inny dla każdego wiersza. Eksperymentuję z rozwiązaniem tej sytuacji. Wyślę to, jeśli sprawię, że zadziała.
manalang

@manalang Czy znalazłeś rozwiązanie dla różnych wysokości dla każdego rzędu?
Wyjątek

1
Innym projektem do sprawdzenia jest infinity.js (dla inspiracji). Jeśli masz dynamiczne elementy wysokości, możesz stworzyć koncepcję „strony”, która jest zbiorem elementów w rzutni. Powiedzmy, że są 3 elementy, a trzeci element jest bardzo długi i rozciąga się poza stronę. Wtedy możesz, powiedzmy, „wysokość strony” to rozmiar trzech największych elementów. Następnie utwórz wirtualne węzły, używając najmniejszej wysokości elementu. A więc var count = pageHeight / minElementHeight. Możesz więc skonstruować 50 elementów, nawet jeśli renderowane są tylko 3, ale to nadal zapewnia dobrą wydajność.
Lance Pollard

14
Nic nie pojawia się na skrzypcach. Pojawiają się przyciski Generuj, ale nic więcej.
grzmot

3
@ sophie-alpert: Czy można zaktualizować jsfiddle? Wiem, że będziesz zajęty, ale jeśli możesz to zaktualizować, skorzysta na tym wielu takich jak ja: D
John Samuel

26

Sprawdź naszą bibliotekę React Infinite:

https://github.com/seatgeek/react-infinite

Aktualizacja grudzień 2016

W rzeczywistości korzystałem ostatnio w wielu moich projektach z react-virtualized i stwierdziłem, że znacznie lepiej radzi sobie z większością przypadków użycia. Obie biblioteki są dobre, zależy to dokładnie od tego, czego szukasz. Na przykład reakcja-wirtualizacja obsługuje pomiary JIT o zmiennej wysokości za pośrednictwem HOC o nazwie CellMeasurer, na przykład tutaj https://bvaughn.github.io/react-virtualized/#/components/CellMeasurer .

Aktualizacja listopad 2018

Wiele lekcji z reaktywowanych wirtualizacji zostało przeniesionych do mniejszej, szybszej i bardziej wydajnej biblioteki okien reagowania tego samego autora.


@jos: użyj tej biblioteki. Spowoduje to usunięcie / dołączenie węzłów DOM, tak jak pojawiają się w widoku.
wle8300

14
Ta biblioteka działa tylko wtedy, gdy znasz wysokości elementów przed renderowaniem.
Druska

1
@Druska, Technicznie tak, ale możesz również użyć okna jako kontenera przewijania za pomocą opcji useWindowAsScrollContainer.
HussienK

Czy biblioteka React-Infinite obsługuje siatki?
user1261710


1
import React, { Component } from 'react';
import InfiniteScroll from 'react-infinite-scroller';


const api = {
    baseUrl: '/joblist'
};

class Jobs extends Component {
    constructor(props) {
            super(props);
            this.state = {
                listData: [],
                hasMoreItems: true,
                nextHref: null
        };
    }

    fetchData(){
            var self = this;           
            var url = api.baseUrl;
            if(this.state.nextHref) {
                url = this.state.nextHref;
            }

            fetch(url)
            .then( (response) => {
                return response.json() })   
                    .then( (json) => {
                        var list = self.state.listData;                        
                        json.data.map(data => {
                            list.push(data);
                        });

                        if(json.next_page_url != null) {
                            self.setState({
                                nextHref: resp.next_page_url,
                                listData: list                               
                            });
                        } else {
                            self.setState({
                                hasMoreItems: false
                            });
                        }
                    })
                    .catch(error => console.log('err ' + error));

        }
    }

    componentDidMount() {
       this.fetchData();
    }

    render() {
    const loader = <div className="loader">Loading ...</div>;
    let JobItems; 
    if(this.state.listData){  
        JobItems = this.state.listData.map(Job => {
        return (
            <tr>
                <td>{Job.job_number}</td>
                <td>{Job.title}</td>
                <td>{Job.description}</td>
                <td>{Job.status}</td>
            </tr>
        );
      });
    }
    return (
      <div className="Jobs">
        <div className="container">
            <h2>Jobs List</h2>

            <InfiniteScroll
                pageStart={0}
                loadMore={this.fetchData.bind(this)}
                hasMore={this.state.hasMoreItems}
                loader={loader}>
                <table className="table table-bordered">
                <thead>
                    <tr>
                        <th>Job Number</th>
                        <th>Title</th>
                        <th>Description</th>
                        <th>Status</th>
                    </tr>
                </thead>
                <tbody>
                {JobItems}
                </tbody>
                </table>
            </InfiniteScroll>
        </div>
    </div>
    );
  }

}

export default Jobs;
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.