0.2.1 • Published 6 years ago

react-virtually v0.2.1

Weekly downloads
4
License
ISC
Repository
github
Last release
6 years ago

react-virtually

React Virtually is a performant virtualized list component for react that works with just in time measured content with dynamic heights. It supports jump to index, and adding/removing of items from the top and bottom of the list (ideal for implementing chat).

Motivation

Rendering large data sets causes performance issues. Optimisation here is key for a healthy user experience. This is especially important on lower end mobile devices and/or when you are dealing with huge data sets. List virtualistion allows us to render only the items we need to show on screen at any given time.

Features

  • Variable item height (measured just in time)
  • Expandable data set. You can prepend or append items to the list and the scroll position won't jump around.
  • Jump to index
  • Achieved through the use of props (no need for refs)

Installation

The react-virtually module is available on NPM: npm install react-virtually

Usage

Basic usage

<InfiniteList
  startIndex={0}
  endIndex={1000}
  itemRendererFactory={ 
    (index:number):JSX.Element => {
      return(<div key={index}
        style={{                  
          padding:10,
        }}>        
         This is item: {index}        
      </div>);
    }  
  }
/>

Props

NameTypeRequiredDescription
startIndexnumbertrueDefines the start index from which to draw rows (the begining of your available data-set - this will typically be 0). Cannot be below 0.
endIndexnumbertrueDefines the end index at which to stop drawing rows (the end of your available data-set - this will usually be the length of your data-set)
jumpToIndexnumberfalseDetermines an index to jump to. This list will jump to the scroll position at which the item will appear as close to the top of the list as possible.
onUserScrolled(scrollTop:number)falseA callback that will be called when a scroll event occurs on the list - passing the scroll top. This is useful if you want to add infinite loading logic.
itemRendererFactory(index:number):JSX.ElementtrueThe item renderer function must return a JSX element for a particular row. This row index will always be within the startIndex and endIndex. At this point you should already have the data loaded for the item. If you want to lazily load data, see lazy loading

Lazy-loading

When dealing with large data sets you probably want to optimize network calls as well as your rendering strategy. To do this with React Virtually you can expand your start and end index whenever you want to append or prepend data to the list. Below is an example of how you might do this for a chat implementation:

export interface ChatMessage {
  readonly message:String;
}

export interface ChatMessages {
  getMessages():ChatMessage[];
}

export class TestChatMessages implements ChatMessages {

  private _messages:ChatMessage[];

  private getRandomMessage():string {
    let baseContent = "This is some message, it has an index. "
    let count = Math.round((Math.random()*10))+1;
    return Array(count).fill(baseContent).toString()
  }

  constructor(total:number=100) {
    this._messages = this._getMessages(total);
  }

  _getMessages(total:number=100):ChatMessage[] {
    return Array(total).fill(0).map((v,index) => {
      let m:ChatMessage = {        
        message: index.toString() +": "+ this.getRandomMessage()
      };
      return m;      
    });
  }
  
  getMessages():ChatMessage[] {
    return this._messages;
  }
}
import React, { Component } from 'react';
import { VirtuallyList } from "react-virtually";
import './App.css';
import { TestChatMessages } from './ChatMessages';

export interface AppState {
  itemIndex?:number  
  startIndex:number,
  endIndex:number
}

export interface AppProps {}

class App extends Component<AppProps,AppState> {

  private messages = new TestChatMessages(100000).getMessages();

  constructor(props:AppProps) {
    super(props);
    this.state = {
      itemIndex: this.messages.length-1, // this will start the list at the bottom      
      startIndex: this.messages.length-50,
      endIndex: this.messages.length
    }
  }

  render() {
    return (      
      <div style={{
        width: 500,
        height: 500
      }}>
      <VirtuallyList
        startIndex={this.state.startIndex}
        endIndex={this.state.endIndex}
        jumpToIndex={this.state.itemIndex}
        onUserScrolled={(top:number) => {
          this.setState({
            itemIndex: undefined
          })
          if(top <= 500) {
            this.setState({
              startIndex: Math.max(this.state.startIndex-50,0)
            })
          }            
        }}
        itemRendererFactory={ 
          (index:number):JSX.Element => {
            let message = this.messages[index];
            return(<div key={index} 
              style={{                  
                padding:10,
              }}>
              <div style={{                          
                backgroundColor: "skyblue",
                padding: 16,        
                borderRadius: "0.4em"
              }}>
                {message.message}
              </div>
            </div>);
          }  
        }
      />        
      </div>
    )
  }
}

export default App;

Tests

Coming soon