Building a Lazy Loader for React

I recently needed to create a Table/List that loads very fast. The main problem of the existing components for lazy loading is that they are not very flexible, children height must be constant and there is a lot of super-fancy (aka complicated) code involved. So i created a much easier one that just loads everything on scrolling – or autoloads everything on interval.

Behold, here is the React Lazy Autoloader: https://www.npmjs.com/package/react-lazy-autoloader

It is easy to install/use, here is a brief explanation how it works:

Autoloading

This is fairly easy. When the component gets mounted, i start an interval with the delay set with the autoLoadInterval  prop:

intervalId = setInterval(this.onLoadMore, this.props.autoLoadInterval);

So every x Seconds, the following function gets called:

onLoadMore = () => {
   if (this.state.loadedChildren.length === this.props.children.length) {
      if (!this.state.loadFinished) {
         this.setState({
            loadFinished: true
         });
      }
      return;
   }
   this.setState({
      loadedChildren: this.props.children.slice(0, this.state.loadedChildren.length + this.props.itemPadding)
   });
};

Ignore the first lines for now, they are just here to detect if all items have been loaded already. In that case, the state variable loadFinished  gets set. The if before setState  could actually be removed, because it´s a PureComponent so there will be no unneccessary call of the render function if loadFinished  did not change.

The Second setState  call just copies a slice of the initial array (that includes ALL children), adding a defined amount of new items. So, what happens here is that the component starts with a low amount of items to show, and adds more and more to the state array. That pretty much sums up the whole concept, and the render function is not very complicated – yes, that´s it already:

render() {
   return (
      
{this.state.loadedChildren}
); }

Side Note: onLoadMore  is a class property, you definitely need babel to transpile the code if you want to use those. They are great though, you don´t need to bind functions anymore 🙂

Scroll-Loading

This is very similar, but it loads additional items only when the user scrolls. Instead of creating an interval , i listen to the scroll event when the component gets mounted:

window.addEventListener('scroll', this.onScroll);

onScroll  is a bit more complicated than onLoadMore  (which is basically just the call to add more items to the state variable):

onScroll = () => {
   if (this.state.loadFinished) {
      return;
   }
   const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;

   let childrenToLoad = this.props.itemPadding + Math.ceil(scrollTop / this.props.childrenHeight);
   childrenToLoad = Math.max(childrenToLoad, this.state.loadedChildren.length);
   childrenToLoad = Math.min(childrenToLoad, this.props.children.length);

   if (childrenToLoad === this.props.children.length) {
      if (!this.state.loadFinished) {
         this.setState({
            loadFinished: true,
            loadedChildren: this.props.children.slice(0, childrenToLoad)
         });
      }
   } else {
      this.setState({
         loadedChildren: this.props.children.slice(0, childrenToLoad)
      });
   }
};

First, if all entries are there already, i just return so nothing will happen. I could also remove the scroll event listener, but i would need to add it again in componentWillReceiveProps …well, that would not be a bad idea, maybe for an update 🙂

Then i get the scroll position, and in the next few lines the amount of children to load. That depends on the scroll position, of course. Math.max  makes sure that the amount of children to load will not get smaller than the amount that is already there (else it would remove children if you scroll up again). Math.min  makes sure that the amount is not higher than the max amount of children.

The rest is similar to autoloading, i just do the check if all items are there already at the end because of timing issues.

That´s it, feel free to comment on this thing, i am happy for input either here or on github (with comments or pull requests to improve the component).

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.