Pengguliran tak terbatas dengan React JS

89

Saya mencari cara untuk mengimplementasikan pengguliran tak terbatas dengan React. Saya telah menemukan react-infinite-scroll dan menganggapnya tidak efisien karena hanya menambahkan node ke DOM dan tidak menghapusnya. Apakah ada solusi yang terbukti dengan React yang akan menambah, menghapus, dan mempertahankan jumlah node yang konstan di DOM.

Inilah masalah jsfiddle . Dalam masalah ini, saya hanya ingin memiliki 50 elemen di DOM pada satu waktu. yang lain harus dimuat dan dihapus saat pengguna menggulir ke atas dan ke bawah. Kami mulai menggunakan React karena algoritme pengoptimalannya. Sekarang saya tidak dapat menemukan solusi untuk masalah ini. Saya telah menemukan js airbnb infinite . Tapi itu diimplementasikan dengan Jquery. Untuk menggunakan gulir tak terbatas airbnb ini, saya harus menghilangkan optimasi React yang tidak ingin saya lakukan.

kode contoh yang ingin saya tambahkan gulir adalah (di sini saya memuat semua item. Tujuan saya adalah memuat hanya 50 item sekaligus)

/** @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'));

Mencari bantuan ...

Rajeev
sumber

Jawaban:

57

Pada dasarnya saat menggulir Anda ingin memutuskan elemen mana yang terlihat dan kemudian merender untuk menampilkan hanya elemen tersebut, dengan satu elemen spacer di atas dan bawah untuk mewakili elemen di luar layar.

Vjeux membuat biola di sini yang dapat Anda lihat di: jsfiddle .

Setelah menggulir itu dijalankan

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
    });
},

dan kemudian fungsi render hanya akan menampilkan baris dalam rentang tersebut displayStart..displayEnd.

Anda juga mungkin tertarik dalam ReactJS: Modeling Bi-Directional Infinite Scrolling .

Sophie Alpert
sumber
2
Ini adalah teknik yang bagus ... terima kasih! Namun, gagal saat recordHeight berbeda untuk setiap baris. Saya bereksperimen dengan perbaikan untuk situasi itu. Saya akan mempostingnya jika saya membuatnya berfungsi.
manalang
@manalang Sudahkah Anda menemukan solusi untuk ketinggian yang berbeda untuk setiap baris?
Pengecualian
1
Proyek lain untuk diperiksa adalah infinity.js (untuk inspirasi). Jika Anda memiliki elemen ketinggian dinamis, maka Anda dapat membuat konsep "halaman" yang merupakan sekumpulan elemen di viewport. Katakanlah ada 3 elemen, dan elemen ketiga sangat panjang dan membentang dari halaman. Kemudian Anda dapat, katakanlah, "tinggi halaman" adalah ukuran dari 3 elemen terbesar. Kemudian buat node virtual menggunakan tinggi elemen terkecil . Jadi var count = pageHeight / minElementHeight. Jadi, Anda dapat membuat 50 elemen, meskipun hanya 3 yang dirender, tetapi itu tetap akan memberi Anda kinerja yang baik.
Lance Pollard
14
Tidak ada yang muncul di biola. Tombol buat muncul, tapi tidak ada yang lain.
guruh
3
@ sophie-alpert: Apakah mungkin memperbarui jsfiddle? Saya tahu Anda akan sibuk, tetapi jika Anda dapat memperbaruinya, itu akan menguntungkan banyak orang seperti saya: D
John Samuel
26

Lihat Perpustakaan React Infinite kami:

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

Perbarui Desember 2016

Saya sebenarnya telah menggunakan react-virtualized di banyak proyek saya baru-baru ini dan menemukan bahwa itu mencakup sebagian besar kasus penggunaan dengan jauh lebih baik. Kedua perpustakaan itu bagus, itu tergantung pada apa yang Anda cari. Misalnya, react-virtualized mendukung pengukuran JIT ketinggian variabel melalui HOC yang dipanggil CellMeasurer, contohnya di sini https://bvaughn.github.io/react-virtualized/#/components/CellMeasurer .

Perbarui November 2018

Banyak pelajaran dari react-virtualized telah ditransfer ke perpustakaan react-window yang lebih kecil, lebih cepat, lebih efisien dari penulis yang sama.

Zach
sumber
@jos: gunakan perpustakaan ini. Ini akan menghapus / menambahkan simpul DOM saat mereka muncul di viewport.
wle8300
14
Pustaka ini hanya berfungsi jika Anda mengetahui ketinggian elemen Anda sebelum rendering.
Druska
1
@Druska, Secara teknis ya, namun Anda juga dapat menggunakan jendela sebagai wadah gulir menggunakan opsi useWindowAsScrollContainer.
HussienK
Apakah jaringan dukungan perpustakaan react-infinite?
pengguna1261710
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;
Sneh
sumber