การเลื่อนไม่มีที่สิ้นสุดด้วย React JS


90

ฉันกำลังมองหาวิธีใช้การเลื่อนแบบไม่มีที่สิ้นสุดด้วย React ฉันเจอreact-infinite-scrollและพบว่ามันไม่มีประสิทธิภาพเพราะมันเพิ่มโหนดไปยัง DOM และไม่ได้ลบออก มีโซลูชันที่พิสูจน์แล้วด้วย React ซึ่งจะเพิ่มลบและรักษาจำนวนโหนดใน DOM ให้คงที่

นี่คือปัญหาjsfiddle ในปัญหานี้ฉันต้องการให้มีองค์ประกอบใน DOM ครั้งละ 50 รายการเท่านั้น อื่น ๆ ควรจะโหลดและลบออกเมื่อผู้ใช้เลื่อนขึ้นและลง เราได้เริ่มใช้ React เนื่องจากอัลกอริทึมการเพิ่มประสิทธิภาพ ตอนนี้ฉันไม่พบวิธีแก้ปัญหานี้ ฉันได้เจอAirbnb js แต่จะใช้กับ Jquery ในการใช้การเลื่อนแบบไม่มีที่สิ้นสุดของ airbnb ฉันต้องคลายการเพิ่มประสิทธิภาพการตอบสนองซึ่งฉันไม่ต้องการทำ

รหัสตัวอย่างที่ฉันต้องการเพิ่มการเลื่อนคือ (ที่นี่ฉันกำลังโหลดรายการทั้งหมดเป้าหมายของฉันคือโหลดครั้งละ 50 รายการเท่านั้น)

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

กำลังต้องการความช่วยเหลือ ...

คำตอบ:


58

โดยทั่วไปเมื่อเลื่อนคุณต้องการตัดสินใจว่าองค์ประกอบใดที่สามารถมองเห็นได้จากนั้นแสดงผลเพื่อแสดงเฉพาะองค์ประกอบเหล่านั้นโดยมีองค์ประกอบตัวเว้นวรรคเดียวที่ด้านบนและด้านล่างเพื่อแสดงองค์ประกอบนอกหน้าจอ

Vjeuxทำไวโอลินที่นี่ที่คุณสามารถดูได้ที่: jsfiddle

เมื่อเลื่อนมันจะดำเนินการ

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

displayStart..displayEndแล้วฟังก์ชั่นการแสดงผลจะแสดงเฉพาะแถวในช่วง

นอกจากนี้คุณยังอาจจะสนใจในReactJS: การสร้างแบบจำลอง Bi-Directional Infinite เลื่อน


2
นี่เป็นเทคนิคที่ยอดเยี่ยม ... ขอบคุณ! อย่างไรก็ตามจะล้มเหลวเมื่อ recordHeight แตกต่างกันสำหรับแต่ละแถว ฉันกำลังทดลองแก้ไขสถานการณ์นั้น ฉันจะโพสต์ให้ถ้าได้ผล
manalang

@manalang คุณพบวิธีแก้ปัญหาความสูงที่แตกต่างกันสำหรับแต่ละแถวหรือไม่?
ข้อยกเว้น

1
อีกโครงการที่ต้องตรวจสอบคือ infinity.js (สำหรับแรงบันดาลใจ) หากคุณมีองค์ประกอบความสูงแบบไดนามิกคุณสามารถสร้างแนวคิดของ "เพจ" ซึ่งเป็นชุดขององค์ประกอบในวิวพอร์ต สมมติว่ามี 3 องค์ประกอบและองค์ประกอบที่ 3 ยาวมากและยืดออกจากหน้า จากนั้นคุณสามารถพูดว่า "ความสูงของหน้า" คือขนาดของ 3 องค์ประกอบที่ใหญ่ที่สุด จากนั้นสร้างโหนดเสมือนโดยใช้ความสูงขององค์ประกอบที่เล็กที่สุด var count = pageHeight / minElementHeightดังนั้น ดังนั้นคุณอาจสร้างองค์ประกอบได้ 50 ชิ้นแม้ว่าจะแสดงผลเพียง 3 ชิ้น แต่ก็ยังให้ประสิทธิภาพที่ดี
Lance Pollard

14
ไม่มีอะไรปรากฏในซอ ปุ่มสร้างปรากฏขึ้น แต่ไม่มีอะไรอื่น
ฤ.

3
@ sophie-alpert: เป็นไปได้ไหมที่จะอัปเดต jsfiddle? ฉันรู้ว่าคุณจะยุ่ง แต่ถ้าคุณสามารถอัปเดตได้ก็จะมีประโยชน์หลายอย่างเช่นฉัน: D
John Samuel

26

ตรวจสอบ React Infinite Library ของเรา:

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

อัปเดตธันวาคม 2559

เมื่อเร็ว ๆ นี้ฉันได้ใช้react-virtualizedในหลายโครงการของฉันและพบว่ามันครอบคลุมกรณีการใช้งานส่วนใหญ่ได้ดีขึ้นมาก ห้องสมุดทั้งสองดีขึ้นอยู่กับสิ่งที่คุณกำลังมองหา ยกตัวอย่างเช่นการตอบสนอง-virtualized สนับสนุนตัวแปรการวัดความสูง JIT ผ่าน HOC เรียกว่าCellMeasurerตัวอย่างที่นี่https://bvaughn.github.io/react-virtualized/#/components/CellMeasurer

อัปเดตเดือนพฤศจิกายน 2561

บทเรียนจำนวนมากจากการทำปฏิกิริยาเสมือนจริงได้ถูกส่งไปยังไลบรารีหน้าต่างตอบกลับที่เล็กกว่าเร็วกว่าและมีประสิทธิภาพมากขึ้นจากผู้เขียนคนเดียวกัน


@jos: ใช้ไลบรารีนี้ มันจะลบ / ต่อท้ายโหนด DOM ตามที่ปรากฏในวิวพอร์ต
wle8300

14
ไลบรารีนี้ใช้งานได้ก็ต่อเมื่อคุณทราบความสูงขององค์ประกอบของคุณก่อนแสดงผล
Druska

1
@Druska ใช่ในทางเทคนิคอย่างไรก็ตามคุณยังสามารถใช้หน้าต่างเป็นที่เก็บเลื่อนโดยใช้ตัวเลือก useWindowAsScrollContainer
HussienK

ไลบรารีตอบสนองไม่มีที่สิ้นสุดรองรับกริดหรือไม่?
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;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.