Code Monkey home page Code Monkey logo

react-virtual-list's Introduction

Super simple virtualized list higher-order component for React version ^15.0.0 || ^16.0.0.

Check out the demo here

react-virtual-list allows you to display a large list of fixed-height items, while only rendering the items visible on the screen. This allows a large list to be rendered with much fewer DOM elements.

Some other benefits:

  • One dependency (and it's prop-types)
  • Small!
  • Performant - demo page almost always stays over 60fps http://i.imgur.com/CHVCK9x.png
  • Keeps your components separate - as a higher-order component
  • Gives you control - doesn't force any particular markup, but gives you the necessary styles and data to use.

Legacy

If you're looking for documentation on version 1, supporting React ~0.13.x, it's here.

Installation

You can use npm to install react-virtual-list.

> npm install react-virtual-list --save

Usage

The ./lib/VirtualList.js module exports a single, ES5-compatible, CommonJS-accessible, component factory.

import VirtualList from 'react-virtual-list';

Your inner list component uses the virtual property to render the visible items, and set a style to set the overall list height and padding.

const MyList = ({
  virtual,
  itemHeight,
}) => (
  <ul style={virtual.style}>
    {virtual.items.map(item => (
      <li key={`item_${item.id}`} style={{height: itemHeight}}>
        Lorem ipsum dolor sit amet
      </li>
    ))}
  </ul>
);

Note: You should set keys on your list items.

Create the virtualized component.

const MyVirtualList = VirtualList()(MyList);

Write the JSX for the virtualized component with the necessary properties.

<MyVirtualList
  items={myBigListOfItems}
  itemHeight={100}
/>

Options

Options are used before the virtualized component can be created. This means that if you need to change an option after the initial render, you will need to recreate the virtualized component.

const options = {
  container: this.refs.container, // use this scrollable element as a container
  initialState: {
    firstItemIndex: 0, // show first ten items
    lastItemIndex: 9,  // during initial render
  },
};

const MyVirtualList = VirtualList(options)(MyList);
Name Type Default Description
container DOM Element window Scrollable element that contains the list. Use this if you have a list inside an element with overflow: scroll.
initialState object - An object with firstItemIndex and lastItemIndex properties, which represent array indexes of items (see below). These are used to calculate the visible items before the component is mounted. Useful for server-side rendering.

Properties

These properties and any others set on your virtual component, such as className, will be passed down to the inner component.

Name Type Default Description
items Array - Full array of list items. Only the visible subset of these will be rendered.
itemHeight Number - Height in pixels of a single item. You must have a CSS rule that sets the height of each list item to this value.
itemBuffer Number 0 Number of items that should be rendered before and after the visible viewport. Try using this if you have a complex list that suffers from a bit of lag when scrolling.

Mapping

VirtualList allows a second, optional, parameter, named mapVirtualToProps, which functions similarly to Redux's mapStateToProps. This function can be provided to change the properties passed to MyList. Its arguments are:

Name Description
props Includes all properties passed to MyVirtualList
state Includes firstItemIndex and lastItemIndex; array indexes of the visible bounds of items

The default mapVirtualToProps can be found here.

Example Usage

Check out demo/src/app.js and demo/src/ConfigurableExample.js for the example used in the demo.

Tests

Use npm test to run the tests using Jest. Not everything is currently tested yet!

react-virtual-list's People

Contributors

aga5tya avatar andrwh avatar developerdizzle avatar haxxxton avatar jordaaash avatar mrchief avatar ramshackle-jamathon avatar vinnymac avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-virtual-list's Issues

New syntax as an HOC

Experimenting with a new architecture as an HOC. I like this as it gives more control to the consumer. One question I've been thinking about is which options, if any, to use as HOC parameters vs properties?

// the HOC

import VirtualList from 'react-virtual-list';

// your component

const MyList = ({
  virtual,
  itemHeight,
}) => (
  <ul style={virtual.style}>
    {virtual.items.map((item) => (
      <li key={`item${item.id}`} style={{height: itemHeight }}>
        Lorem ipsum dolor sit amet
      </li>
    ))}
  </ul>
);

// using the HOC - which options, if any, should go in here?

const options = {
  scrollThrottle: 1000,
};

const MyVirtualList = VirtualList(options)(MyList);

// rendering - which options should be props here?

<MyVirtualList
  items={myBigListOfItems}
  container={window}
  itemBuffer={0}
  itemHeight={100}
/>

[Bug/Feature Request] Resize Event Listener not appropriate when container option not window

Currently VirtualList attaches to the 'resize' event on the supplied container option.

This is fine when window is supplied as the container, but will not fire when an alternate element is supplied.

There are a number of approaches for detecting element size change of non-window elements that could be integrated to correctly trigger this.refreshState.

Happy to submit a PR if you wanted to choose which lib you liked best.

throttleWithRAF causes setState warning

Unmounting a component that uses react-virtual-list may cause it to produce the following warning:

Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the vlist component.

If I disable throttleWithRAF, the warning is gone, because there is no longer any asynchronicity. The vlist component should not call setState if it is no longer mounted.

Only rendering a single item

Below is the code which I'm using to integrate your component.

const MyList = ({
virtual,
itemHeight,
}) => (


{virtual.items.map(item => (
<div key={item_${item.id}} style={{height: 240}}>
{item}

))}

);
const MyVirtualList = VirtualList()(MyList);

and after that I'm using 'MyVirtualList' inside my render.
<MyVirtualList items={onwFlightsCopy} itemHeight={240}/>

onwFlightsCopy is a huge list of react elements (>400).

I tried putting some debugger and found out my 'firstItemIndex' and 'lastItemIndex' is always coming as 0 and 1. Can you please let me know why this is being set faulty ?

Add license?

This looks like a handy component to use, but I'm very hesitant to actually utilize it since without an explicit license this source code defaults to "All Rights Reserved".

1,000,000 record can't render

i try filling 1,000,000 records and then i scroll down, its just can render about 400,000 records, do you thing that problem is because of padding-top is too big ?

VirtualScroller and forceUpdateGrid do not call rowHeight function

Hi! I love your project, its awesome!!!

I think I've run into a bug, or I'm doing something incorrectly because I can't get forceUpdateGrid to run the rowHeight function.

I have a list of components that can be expanded to see more info at which point i try to call the forceUpdateGrid function but it doesn't have an effect.

import React from 'react'

import { VirtualScroll,AutoSizer } from 'react-virtualized';

import DealroomBuilding from '../../containers/broker_dashboard/DealroomBuilding';

const DealroomStateList = React.createClass({

    getInitialState(){
        return {
            buildings_expanded: {},
            buildings_hiding_listings: {},
           row_count: this.props.building_ids.length,
        }
    },





_onBuildingExpandToggle(b_id, is_expanded){
    var self = this;

    var buildings_expanded = this.state.buildings_expanded;
    buildings_expanded[b_id] = is_expanded;

    this.setState({buildings_expanded}, 
        ()=>{

            self.refs.auto_sizer.refs.scroller.forceUpdateGrid();
        }
    );

},

_onShowListingsToggle(b_id, is_showing){
    var self = this;

    var buildings_hiding_listings = this.state.buildings_hiding_listings;
    buildings_hiding_listings[b_id] = !is_showing;

    //hack because i can't get virtual scroller to forceupdate (the index check part) 
    //  so i have to update one of the properties of the virtualscroller
    var row_count = this.state.row_count+1;


    this.setState({buildings_hiding_listings, row_count}, 
        ()=>{
            self.setState({
                row_count: row_count-1,
            })
        }
    );
},


_getVirtualScrollRow({index, isScrolling}){
    if(index === 0){
        return this._getDLCounterHtml();
    }
    return this._getDealroomBuildingHtml(index-1);
},
_getDLCounterHtml(){
    return(
        <div>
            <div className='content-title' style={{'display': 'inline-block'}}>
                {this.props.dl_count} {this.props.title}
            </div>
            <div style={{'display': 'inline-block'}}> 
                {this.props.children?
                    <span >   
                        {this.props.children}
                    </span>
                    :
                    null
                }
            </div>
        </div>
    )
},
_getDealroomBuildingHtml(index){
    const building_ids = this.props.building_ids;


    const b_id = building_ids[index];

    return(
        <DealroomBuilding key={b_id} id={b_id}
            onBuildingExpandToggle={this._onBuildingExpandToggle} 
            onShowListingsToggle={this._onShowListingsToggle}
        />
    )


},

_getRowHeight({index}){
    if(index === 0 || index > this.props.building_ids.length){
        return 50;
    }

    const{
        buildings_expanded,
        buildings_hiding_listings,
    } = this.state;

    const b_id = this.props.building_ids[index - 1];

    var height = 39;

    if(buildings_expanded[b_id]){
        height += 193;
    }
    else{
        height += 59;
    }

    if(!buildings_hiding_listings[b_id]){
        return height + 100;
    }


    return height



},

render(){
    return(
        <div className='content'>
            <AutoSizer ref='auto_sizer'>
                {({ height, width }) =>{ return(
                    <VirtualScroll
                    ref='scroller'
                    rowCount={this.state.row_count + 1}
                    rowHeight={this._getRowHeight}
                    rowRenderer={this._getVirtualScrollRow}
                    height={height}
                    width={width}
                  />
                )}}
              </AutoSizer>

        </div>  
    )
}
});

export default DealroomStateList;

the hack I've used to get around this is to have rowCount as a state variable which i increment then decrement to force the virtualscroller to have to rerender since its props change.

The hack is being used in _onShowListingsToggle. When that function is called everything works perfectly and the list is resized appropriately.

However when _onBuildingExpandToggle is called I call forceGridUpdate and nothing happens. I put a break point inside _getRowHeight, but its never called

Data list not render all of them.

I have a more 1000 items . But when I rendered use VirtualList not show all of item I have
this my screenshot : http://pasteboard.co/4aGIaY5Dy.jpg

`render(){

    const MyList = ({
      virtual,
      itemHeight,
    }) => (
      <ul style={virtual.style}>
        {virtual.items.map(item => (
          <li key={`item_${item.id}`} style={{height: itemHeight}}>
            Lorem ipsum dolor sit amet
          </li>
        ))}
      </ul>
    );
    const MyVirtualList = VirtualList()(MyList);
    
return (
      <div >
          
        <MyVirtualList
          items={this.state.sourceList}
          itemHeight={10}
        />
      </div>
  )

}`

Documentation/Feature Request

Pulled out my hair over this for more than an hour.

The container of the list MUST have "box-sizing: border-box;" set, or the padding will contribute to the container's height and the whole thing breaks down.

Would be nice if the lib injected this style or you mentioned it in the documentation. :)

If it helps my implementation looks like this:

this.list = VirtualList()(({
    virtual,
    itemHeight,
}) => (
    <div style={{boxSizing: "border-box", ...virtual.style}}>
        {virtual.items.map((prod, idx) => (
            <div style={{height: itemHeight}} key={idx}>{new MenuItem(prod, this.state.menu.catTypes[prod.mainCategory as string], this.state.menu.locationOrdering).render()}</div>
        ))}
    </div>
));

Support containers other than window

Currently only calculates item visibility against window object. Allow a container element to be specified in props, so that a scrollable div can be used as well.

Broken on Internet Explorer

Tested with IE10 and 11. The issue is this line:

var viewTop = typeof container.scrollY !== 'undefined' ? container.scrollY : container.scrollTop;

If container is window, scrollY and scrollTop are undefined in IE, so container.pageYOffset must be used instead.

I'm testing a fix now and will PR.

Throttle onScroll with requestAnimationFrame

Related to #21. Right now, props.scrollDelay is used to throttle the internal onScroll handler.

The way I do this in my resize handlers is to use requestAnimationFrame. They aren't necessarily mutually exclusive -- requestAnimationFrame could be used on the debounced handler. This could improve perceived rendering performance significantly.

Right now the only dependency of the library is React, but I think it could be worthwhile to use chrisdickinson/raf or another polyfill here.

no dist dir

hi, I am using "version": "2.0.0"

  1. did not have dist dir

  2. observable list is not an array obj, when I use mobx


image

image

Warning: Failed prop type: Invalid prop `items` of type `object` supplied to `vlist`, expected `array`.
    in vlist (created by News)
    in News (created by inject-News-with-newsStore)
    in inject-News-with-newsStore (created by Route)
    in Route (created by Home)
    in div (created by Home)
    in Home (created by inject-Home-with-newsStore)
    in inject-Home-with-newsStore (created by Route)
    in Route (created by App)
    in div (created by App)
    in Router (created by HashRouter)
    in HashRouter (created by App)
    in App
    in Provider
    in AppContainer

Optimize performance of getVirtualItems

getVirtualState is getting the height of the container every onScroll (in addition to a lot of other repeated calculations) when the values likely haven't changed. I believe this is causing elements to be reflowed by the browser, and even if not, is doing the same work over and over.

I will try to optimize the method after #14 which is required to know if things need to be recalculated.

Client-side display items starting from...

I need to make the first visible item to have some predefined index. This is my code and it doesn't work, the first visible item always has index of zero

import React from 'react';

import VirtualList from 'react-virtual-list';

const List = ({ virtual, itemHeight }) => (
  <ul style={virtual.style}>
    {virtual.items.map(item => (
      <li key={`item_${parseInt(item, 10)}`} style={{ height: itemHeight }}>
        {item}
      </li>
    ))}
  </ul>
);

class Slides extends React.PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      slides: [
        '00', '01', '02', '03', '04', '05', '06', '07', '08', '09',
        '10', '11', '12', '13', '14', '15', '16', '17', '18', '19',
        '20', '21', '22', '23', '24', '25', '26', '27', '28', '29',
      ],
    };

    const options = {
      initialState: {
        firstItemIndex: 10,
        lastItemIndex : 20
      }
    };

    this.ListSlides = VirtualList(options)(List);
  }

  render() {
    const ListKtuviot = this.ListSlides;

    return (
      <ListKtuviot items={this.state.slides} itemHeight={50} />
    );
  }
}

Slides.propTypes = {};

export default Slides;

Is it possible to set containerHeight 100% instead of a fixed size?

Hey,

Thank you for the plugin.

I am trying to create something like this example: https://shakedos.com/examples/list/pwa.php. I was wondering if it would be possible to set the containerHeight to 100% so that it will automatically fit the size that is required.

Not sure if it's interesting, but that's the reason I am trying to do that: https://stackoverflow.com/questions/45824080/memory-issue-with-long-lists-in-a-web-app

Thank you again,
Shaked

Method shouldComponentUpdate is not good for some cases

I have virtual list and I store selected row in component state. If selected row is changed, virtual list not rerender content, because metod shouldComponentUpdate compares only bufferStart, height and items equality, but I store selected item separately. Is there are any way to tell rerender virtual list component?

Thank you.

The readme should reflect that the element that receives virtual.style needs box-sizing: border-box

I've spent some time trying to figure out why my react-virtual-list implementation kept scrolling down after I reached the bottom, seemingly increasing the list's size the further I scrolled. After I dug into how this library works, at some point it became really obvious what the problem was: With each list item vanishing above the viewport, more padding was added to the list, which in turn was added to the list's height because I hadn't set box-sizing: border-box on it previously.

If this requirement was included somewhere in the readme, that might prevent some pain getting started. Otherwise, really good job with this!

Handle resizing

Right now, getVirtualState is called in 4 places: getInitialState, componentWillReceiveProps, componentDidMount, and onScroll. If the window is resized, or the orientation changes on mobile, this causes the list to display too few or too many items, depending on the change.

A listener on the window for resize and orientation change events to call getVirtualState would fix this (I do this my own wrapper for VirtualList in my application). I can PR if you're interested!

Added elements is not render

Hi! I'am trying to add new elements at list, but added elements is not rendered.
In screenshot the result of render function with my refreshing list and virtual argument

const MyList = ({ virtual, itemHeight, section }) => {
  console.log(virtual)
  return (
    <ListStyled>
      {virtual.items.map((row, index) => (
        <div key={index} style={{ display: 'flex'}}>
          {row.map((material) => (
            <MaterialListItem
              key={material.get('id')}
              material={material}
              section={section}
            />
          ))}
        </div>
      ))}
    </ListStyled>
)};

 splitIntoChunks = (list, chunkSize = 4) => {
    return Range(0, list.count(), chunkSize)
      .map(chunkStart => list.slice(chunkStart, chunkStart + chunkSize));
  }


  render() {
    const items = this.splitIntoChunks(this.props.materials).toList().toArray()
    console.log(items)
    const MyVirtualList = this.MyVirtualList;
    return (
      <div>
        <MyVirtualList
          items={items}
          itemHeight={130}
          section={this.props.section}
        />
...

2017-08-25 16 55 31

Using container element the right way

VirutalList attempts to directly add the needed event listeners directly to the DOM element. I have found this can cause the error Cannot read property addEventListener of null in certain cases, because componentDidMount on the virtual list is being called before the parent has rendered.

Is there any obvious work-around for this or is there something wrong with our lifecycle management?

Variable Heights?

Just a feature request, but any change there is a variable height component prop for this?

Check state

You don't check whether state from getVisibleItemBounds() is undefined before comparison:
problem

Load in items?

Is it possible to load in items upon different scroll events or something?

React 0.14 support

I'm back and finally have some free time this upcoming week, so I'll be tackling my other open issues and PRs. Sorry for the cruft!

npm install --save react react-dom
npm WARN unmet dependency ./node_modules/react-virtual-list requires react@'^0.13.3' but will load
npm WARN unmet dependency ./node_modules/react,
npm WARN unmet dependency which is version 0.14.0

Force recalculation

First of all, very nice work, I like your virtual-list, it's very usefull and super efficient.

I just has a suggestion: have a way to force the rendering of the list, even if we don't scroll.

I'm using your list to show a big list of complex components. On each of this component I have a check box (to be able to select it and make later an action). Outside of the virtual-list, I have "select-all" button. When I click on it my items array is updated and all the items are marked as checked, but the Virtualist is not updated, because I didn't scroll it yet.

Is there a way to improve this?
Thanks

Server rendering

Right now, getInitialState provides an empty array for state.items because of this check.

While this makes sense in the context, the component must render twice (once after componentWillMount with no items, and then again when the state changes when the calculated height is known on componentDidMount). This causes a flash of the empty list beforehand, and also means that server side rendering doesn't work. What do you think of having a property to provide a number of items that will be rendered on the initial render, and then when componentDidMount runs, rendered selectively as usual?

This way, it can render enough items to generally fill the screen on the server, the client will render the same items (avoiding a hash mismatch), and then the list will get updated dynamically from that point on. I'm happy to work on a PR if you're interested.

Updating the table while maintaining the scroll position

Hi,

Thanks for taking the time to write this module.

I'm trying to adapt your list component to my use-case; I'm trying to build a real-time data feed where new messages are prepended to the top of the list. Prepending is easy, I simply rebuild the list in your update function from the test example, and call setState().

But I would also like to maintain the scroll position so the list doesn't jitter while the user is reading. Basically, if the top-most box is currently visible in the viewport, then allow the render function to proceed (because the user is viewing the top of the list). If the user is scrolling somewhere further down the list, then don't render. The problem is I don't know how to check if the first item from the list is currently displayed in the DOM. Is there a way to extract the current set of items being rendered? How do I extract the "key={item.id}" from the rendered boxes?

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.