Cuộn vô hạn với React JS


89

Tôi đang tìm cách triển khai tính năng cuộn vô hạn với React. Tôi đã xem qua react-vô hạn-scroll và thấy nó không hiệu quả vì nó chỉ thêm các nút vào DOM và không xóa chúng. Có giải pháp nào đã được chứng minh với React sẽ thêm, xóa và duy trì số lượng nút không đổi trong DOM.

Đây là vấn đề jsfiddle . Trong vấn đề này, tôi muốn chỉ có 50 phần tử trong DOM cùng một lúc. những người khác sẽ được tải và xóa khi người dùng cuộn lên và xuống. Chúng tôi đã bắt đầu sử dụng React vì các thuật toán tối ưu hóa của nó. Bây giờ tôi không thể tìm thấy giải pháp cho vấn đề này. Tôi đã xem qua airbnb js vô hạn . Nhưng nó được thực hiện với Jquery. Để sử dụng cuộn vô hạn airbnb này, tôi phải bỏ qua việc tối ưu hóa React mà tôi không muốn làm.

mã mẫu tôi muốn thêm cuộn là (ở đây tôi đang tải tất cả các mục. Mục tiêu của tôi là chỉ tải 50 mục cùng một lúc)

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

Tim sự giup đơ...

Câu trả lời:


57

Về cơ bản khi cuộn, bạn muốn quyết định phần tử nào hiển thị và sau đó kết xuất để chỉ hiển thị các phần tử đó, với một phần tử đệm đơn ở trên và dưới để đại diện cho các phần tử ngoài màn hình.

Vjeux đã thực hiện một trò chơi ở đây mà bạn có thể xem: jsfiddle .

Khi cuộn, nó thực thi

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

và sau đó hàm kết xuất sẽ chỉ hiển thị các hàng trong phạm vi displayStart..displayEnd.

Bạn cũng có thể quan tâm đến ReactJS: Lập mô hình cuộn vô hạn theo hai hướng .


2
Đây là một kỹ thuật tuyệt vời ... thx! Tuy nhiên, nó không thành công khi recordHeight khác nhau cho mỗi hàng. Tôi đang thử nghiệm một giải pháp cho tình huống đó. Tôi sẽ đăng nó nếu tôi làm cho nó hoạt động.
manalang

@manalang Bạn đã tìm thấy giải pháp cho chiều cao khác nhau cho mỗi hàng chưa?
Ngoại lệ,

1
Một dự án khác để kiểm tra là infinity.js (để lấy cảm hứng). Nếu bạn có các phần tử chiều cao động, thì bạn có thể tạo khái niệm "trang" là một tập hợp các phần tử trong chế độ xem. Giả sử có 3 phần tử, và phần tử thứ ba siêu dài và trải dài ra khỏi trang. Sau đó, bạn có thể nói, "chiều cao trang" là kích thước của 3 phần tử lớn nhất. Sau đó, xây dựng các nút ảo bằng cách sử dụng chiều cao phần tử nhỏ nhất . Vì vậy var count = pageHeight / minElementHeight. Vì vậy, bạn có thể tạo 50 phần tử, mặc dù chỉ có 3 phần tử được hiển thị, nhưng điều đó vẫn mang lại cho bạn hiệu suất tốt.
Lance Pollard

14
Không có gì xuất hiện trong fiddle. Nút tạo xuất hiện, nhưng không có gì khác.
sấm

3
@ sophie-alpert: Có thể cập nhật jsfiddle không? Tôi biết bạn sẽ bận rộn, nhưng nếu bạn có thể cập nhật nó, nó sẽ có lợi cho nhiều người giống như tôi: D
John Samuel

26

Kiểm tra Thư viện React Infinite của chúng tôi:

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

Cập nhật tháng 12 năm 2016

Tôi thực sự đã sử dụng phản ứng ảo hóa trong rất nhiều dự án của mình gần đây và thấy rằng nó bao gồm phần lớn các trường hợp sử dụng tốt hơn rất nhiều. Cả hai thư viện đều tốt, nó phụ thuộc vào chính xác những gì bạn đang tìm kiếm. Ví dụ: phản ứng ảo hóa hỗ trợ đo chiều cao thay đổi JIT thông qua HOC được gọi CellMeasurer, ví dụ ở đây https://bvaughn.github.io/react-virtualized/#/components/CellMeasurer .

Cập nhật tháng 11 năm 2018

Rất nhiều bài học từ phản ứng ảo hóa đã được chuyển sang thư viện cửa sổ phản ứng nhỏ hơn, nhanh hơn, hiệu quả hơn từ cùng một tác giả.


@jos: sử dụng thư viện này. Nó sẽ xóa / nối các nút DOM khi chúng xuất hiện trong khung nhìn.
wle8300

14
Thư viện này chỉ hoạt động nếu bạn biết chiều cao của các phần tử trước khi hiển thị.
Druska

1
@Druska, Về mặt kỹ thuật là có, tuy nhiên bạn cũng có thể sử dụng cửa sổ làm vùng chứa cuộn bằng tùy chọn useWindowAsScrollContainer.
HussienK

Thư viện phản ứng vô hạn có hỗ trợ lưới không?
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;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.