React JS를 사용한 무한 스크롤


90

React로 무한 스크롤을 구현하는 방법을 찾고 있습니다. 나는이 건너 반응 - 무한 스크롤을 하고 비효율적이 바로 DOM에 노드를 추가하고 제거하지 않는 한 발견했다. DOM에서 일정한 수의 노드를 추가, 제거 및 유지하는 React로 입증 된 솔루션이 있습니까?

다음은 jsfiddle 문제입니다. 이 문제에서는 한 번에 DOM에 50 개의 요소 만 갖고 싶습니다. 사용자가 위아래로 스크롤 할 때 다른 것들은로드되고 제거되어야합니다. 우리는 최적화 알고리즘 때문에 React를 사용하기 시작했습니다. 이제이 문제에 대한 해결책을 찾을 수 없습니다. 나는 airbnb infinite js만났습니다 . 그러나 Jquery로 구현됩니다. 이 에어 비앤비 무한 스크롤을 사용하려면 내가하고 싶지 않은 React 최적화를 풀어야합니다.

스크롤을 추가하고 싶은 샘플 코드는 (여기에서는 모든 항목을로드하고 있습니다. 내 목표는 한 번에 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
    });
},

그러면 render 함수는 범위의 행만 표시합니다 displayStart..displayEnd.

ReactJS : Modeling Bi-Directional Infinite Scrolling에 관심이있을 수도 있습니다 .


2
이것은 훌륭한 기술입니다 ... thx! 그러나 각 행에 대해 recordHeight가 다른 경우 실패합니다. 나는 그 상황에 대한 수정을 실험하고 있습니다. 작동하면 게시하겠습니다.
manalang 2014-06-26

@manalang 각 행의 높이가 다른 솔루션을 찾았습니까?
예외

1
확인해야 할 또 다른 프로젝트는 infinity.js (영감 용)입니다. 동적 높이 요소가있는 경우 뷰포트의 요소 집합 인 "페이지"개념을 만들 수 있습니다. 3 개의 요소가 있고 세 번째 요소는 매우 길고 페이지에서 뻗어 있다고 가정합니다. 그러면 "페이지 높이"가 가장 큰 3 개 요소의 크기라고 말할 수 있습니다. 그런 다음 가장 작은 요소 높이를 사용하여 가상 노드를 구성합니다 . 그래서 var count = pageHeight / minElementHeight. 따라서 3 개만 렌더링 되더라도 50 개의 요소를 구성 할 수 있지만 여전히 좋은 성능을 제공합니다.
Lance Pollard

14
바이올린에 아무것도 나타나지 않습니다. 생성 버튼이 나타나지만 다른 것은 없습니다.
thund dec

3
@ sophie-alpert : jsfiddle을 업데이트 할 수 있습니까? 바쁠 것임을 알고 있지만 업데이트 할 수 있다면 저와 같은 많은 사람들에게 도움이 될 것입니다. D
John Samuel

26

React Infinite 라이브러리를 확인하십시오.

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

2016 년 12 월 업데이트

실제로 최근 많은 프로젝트에서 react-virtualized 를 사용하고 있으며 대부분의 사용 사례를 훨씬 더 잘 다루고 있습니다. 두 라이브러리 모두 훌륭하며 정확히 무엇을 찾고 있는지에 따라 다릅니다. 예를 들어 react-virtualized는라는 HOC를 통해 가변 높이 JIT 측정을 지원합니다 ( CellMeasurer예 : https://bvaughn.github.io/react-virtualized/#/components/CellMeasurer) .

2018 년 11 월 업데이트

react-virtualized에서 얻은 많은 교훈 은 동일한 작성자 의 더 작고 빠르며 효율적인 react-window 라이브러리 로 이식되었습니다 .


@jos :이 라이브러리를 사용합니다. 뷰포트에 나타나는 DOM 노드를 제거 / 추가합니다.
wle8300

14
이 라이브러리는 렌더링하기 전에 요소의 높이를 알고있는 경우에만 작동합니다.
Druska

1
@Druska, 기술적으로 그렇습니다. 그러나 useWindowAsScrollContainer 옵션을 사용하여 창을 스크롤 컨테이너로 사용할 수도 있습니다.
HussienK

react-infinite 라이브러리가 그리드를 지원합니까?
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.