Desplazamiento infinito con React JS

89

Estoy buscando formas de implementar el desplazamiento infinito con React. Me encontré con react-infinite-scroll y lo encontré ineficiente ya que solo agrega nodos al DOM y no los elimina. ¿Existe alguna solución probada con React que agregue, elimine y mantenga un número constante de nodos en el DOM?

Aquí está el problema de jsfiddle . En este problema, quiero tener solo 50 elementos en el DOM a la vez. otros deben cargarse y eliminarse a medida que el usuario se desplaza hacia arriba y hacia abajo. Comenzamos a usar React debido a sus algoritmos de optimización. Ahora no pude encontrar una solución a este problema. Me he encontrado con airbnb infinite js . Pero está implementado con Jquery. Para usar este desplazamiento infinito de Airbnb, tengo que perder la optimización de React que no quiero hacer.

El código de muestra que quiero agregar es el desplazamiento (aquí estoy cargando todos los elementos. Mi objetivo es cargar solo 50 elementos a la vez)

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

En busca de ayuda...

Rajeev
fuente

Respuestas:

57

Básicamente, al desplazarse, desea decidir qué elementos son visibles y luego volver a renderizar para mostrar solo esos elementos, con un solo elemento espaciador en la parte superior e inferior para representar los elementos fuera de la pantalla.

Vjeux hizo un violín aquí que puede ver: jsfiddle .

Al desplazarse se ejecuta

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

y luego la función de render mostrará solo las filas en el rango displayStart..displayEnd.

También puede estar interesado en ReactJS: Modelado de desplazamiento infinito bidireccional .

Sophie Alpert
fuente
2
Esta es una gran técnica ... ¡gracias! Sin embargo, falla cuando recordHeight es diferente para cada fila. Estoy experimentando con una solución para esa situación. Lo publicaré si consigo que funcione.
manalang
@manalang ¿Ha encontrado una solución para diferentes alturas para cada fila?
Excepción del
1
Otro proyecto para revisar es infinity.js (para inspirarse). Si tiene elementos de altura dinámicos, puede crear el concepto de una "página", que es un conjunto de elementos en la ventana gráfica. Digamos que hay 3 elementos y el tercer elemento es muy largo y se extiende fuera de la página. Entonces puede, digamos, "altura de página" es el tamaño de los 3 elementos más grandes. Luego construya nodos virtuales usando la altura de elemento más pequeña . Entonces var count = pageHeight / minElementHeight. Por lo tanto, puede construir 50 elementos, aunque solo se rendericen 3, pero eso aún le dará un buen rendimiento.
Lance Pollard
14
No aparece nada en el violín. Aparecen los botones Generar, pero nada más.
thund
3
@ sophie-alpert: ¿Es posible actualizar jsfiddle? Sé que estará ocupado, pero si puede actualizarlo, beneficiaría a muchos como yo: D
John Samuel
26

Consulte nuestra biblioteca React Infinite:

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

Actualización de diciembre de 2016

De hecho, he estado usando react-virtualized en muchos de mis proyectos recientemente y descubrí que cubre la mayoría de los casos de uso mucho mejor. Ambas bibliotecas son buenas, depende exactamente de lo que estés buscando. Por ejemplo, react-virtualized admite la medición JIT de altura variable a través de un HOC llamado CellMeasurer, ejemplo aquí https://bvaughn.github.io/react-virtualized/#/components/CellMeasurer .

Actualización de noviembre de 2018

Muchas de las lecciones de react-virtualized se han trasladado a la biblioteca de ventana de reacción más pequeña, rápida y eficiente del mismo autor.

Zach
fuente
@jos: usa esta biblioteca. Eliminará / agregará nodos DOM a medida que aparezcan en la ventana gráfica.
wle8300
14
Esta biblioteca solo funciona si conoce las alturas de sus elementos antes de renderizar.
Druska
1
@Druska, técnicamente sí, sin embargo, también puede usar la ventana como contenedor de desplazamiento usando la opción useWindowAsScrollContainer.
HussienK
¿La biblioteca react-infinite admite cuadrículas?
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;
Sneh
fuente