Deepak Kothari
Deepak Kothari

Reputation: 1753

Implement custom infinite scrolling in react

am trying to implement infinite scrolling but as the component re-renders the scroll bar goes back all the way to the top as shown below. How to stick scroll bar to where user has scrolled? or is this because component re-renders? But the component has to re-render because new records are added from the api call. enter image description here

AppList.tsx

  //Initial values
  const [pageNumber, setPageNumber] = useState<number>(1);
  const [scrolling, setScrolling] = useState<boolean>(false);
  const [totalPages, setTotalPages] = useState<number>(3);

<div ref={divRef} className={AppList.Container} onScroll={() => {
        if (scrolling && totalPages! <= pageNumber!) {
          return;
        }
        else {
          const nodes = document.querySelectorAll('.ms-List-cell');
          const lastLi: Element = nodes[nodes.length - 1];
          const lastLiOffset = lastLi.getBoundingClientRect().top + lastLi.clientHeight;
          const pageOffset = window.pageYOffset + window.innerHeight;
          var bottomOffset = 150;
          if (pageOffset > lastLiOffset - bottomOffset) {
            setScrolling!(true);
            setPageNumber!(pageNumber! + 1);
            dispatch(GetAllAppDefinitions(dispatch, pageNumber! + 1));(Service call)
          }
        }
      }}>
        {updatedColumns.length > 0 && (
          <DetailsList
            className={appListTableStyle}
            columns={updatedColumns}
            selectionMode={SelectionMode.none}
            items={updatedItems || []}
          />
        )}
      </div>

Upvotes: 1

Views: 4270

Answers (3)

Jose Greinch
Jose Greinch

Reputation: 458

you can check my implementation on this medium article you don't need to use onScroll at all!

my guess is that if you bar is going to the top probably you are mounting/unmounting the component that list the items. What you would need to do is just progressively load the items. If the component that list the items stays mounted no scrolling should happen :)

Upvotes: -1

Yurii H
Yurii H

Reputation: 337

This is how I implemented my infinite scroll. scrollElement is setting in case if you scrolling on inner element instead of window

import React from 'react';
import PropTypes from 'prop-types';

componentDidMount() {
  const {
    initialLoad,
    initialPage,
    loadMore,
    scrollElement
  } = this.props;
    (scrollElement || window).addEventListener('scroll', this.handleScroll);
    if (initialLoad) {
      loadMore(initialPage);
    }
  }

  componentWillUnmount() {
    const { scrollElement } = this.props;
    (scrollElement || window).removeEventListener('scroll', this.handleScroll);
  }

  handleScroll = () => {
    const {
      loadMore, threshold, hasMore, loading, page, scrollElement
    } = this.props;
    if (scrollElement) {
      const contentHeight = document.getElementById('scroller').clientHeight;
      const {  scrollTop } = scrollElement;
      const { y, height } = scrollElement.getBoundingClientRect();
      if (((height - y) + scrollTop) > contentHeight - threshold && hasMore && !loading) {
        loadMore(page);
      }
      return;
    }
    const { innerHeight, scrollY } = window;
    const contentHeight = document.getElementById('scroller').clientHeight;
    if (innerHeight + scrollY > contentHeight - threshold && hasMore && !loading) {
      loadMore(page);
    }
  };

  render() {
    const {
      loading, children, loader, className
    } = this.props;
    return (
      <div
        id="scroller"
        className={ className }
      >
        { children }
        { loading ? loader : null }
      </div>
    );
  }

}

Upvotes: 1

user14361391
user14361391

Reputation:

Here is a basic example of infinite scrolling using React Hooks, you can modify it according to your need to make an API request and push it to your data,

import React, { useEffect, useState, useRef  } from 'react';

const divStyle = {
    color: 'blue',
    height: '250px',
    textAlign: 'center',
    padding: '5px 10px',
    background: '#eee',
    marginTop: '15px'
};


const containerStyle = {
    maxWidth: '1280px',
    margin: '0 auto',
}
const InfiniteScroll = () => {
    const [postList, setPostList] = useState({
        list: [1,2,3,4]
    }); 
    // tracking on which page we currently are
    const [page, setPage] = useState(1);
    // add loader refrence 
    const loader = useRef(null);

    useEffect(() => {
         var options = {
            root: null,
            rootMargin: "20px",
            threshold: 1.0
         };
        // initialize IntersectionObserver
        // and attaching to Load More div
         const observer = new IntersectionObserver(handleObserver, options);
         if (loader.current) {
            observer.observe(loader.current)
         }

    }, []);


    useEffect(() => {
        // here we simulate adding new posts to List
        const newList = postList.list.concat([1,1,1,1]);
        setPostList({
            list: newList
        })
    }, [page])

    // here we handle what happens when user scrolls to Load More div
   // in this case we just update page variable
    const handleObserver = (entities) => {
        const target = entities[0];
        if (target.isIntersecting) {   
            setPage((page) => page + 1)
        }
    }


    return (<div className="container" style={containerStyle}>
        <div className="post-list">
            {
                postList.list.map((post, index) => {
                    return (<div key={index} className="post" style={divStyle}>
                        <h2> {post } </h2>
                    </div>)
                })
            }
             <!-- Add Ref to Load More div -->
            <div className="loading" ref={loader}>
                    <h2>Load More</h2>
           </div>
        </div>
    </div>)
}

export default InfiniteScroll;

Credits to => https://dev.to/hunterjsbit/react-infinite-scroll-in-few-lines-588f

Upvotes: 2

Related Questions