React-beginner
React-beginner

Reputation: 115

How to avoid scrollbar staying on top when loading more data (infinite scroll) in React

I'm making an infinite scroll up of the chat room, judging to see the last message and loading the data, but the scrollbar remains at the top of the page, you know, it will automatically execute the call api infinitely,how to keep the scrollbar at the previous message in the position

Upvotes: 4

Views: 6960

Answers (2)

Sanchitos
Sanchitos

Reputation: 8589

This solution is for library https://github.com/ankeetmaini/react-infinite-scroll-component

The structure I used is based on props passed to a child component. Parent component is doing the fetching and controlling the state.

Parent Component:

const fetchScrollData = () => {
    // Here we set that we are not able to do scrolling fetching
    setIsLoadingScrollData(true);
    // Apply here some pagination calculation
    searchData()
        .then((tempData) => {
         // Here i am saving the id of the first message
         // I had before scrolling
          setShouldScrollToId(data[0].id);
          const newData = tempData.concat(data);
          setData(newData);
          // Apply here some logic, depending if you have finished or not
          setHasMoreData(true);
        })
        .then(() => {
          setTimeout(() => {
            // Change the timeout accordingly
            // hack for setting the isloading to false when everything is done
            setIsLoadingScrollData(false);
          }, 1500);
        });
  };

Child Component

type PropsType = {
  fetchScrollData: () => void;
  hasMoreData: boolean;
  data: string[];
  isLoadingScrollData: boolean;
  shouldScrollToId: string;
};

const ChildComponent: FC<PropsType> = (props: PropsType) => {
  useEffect(() => {
    if (props.shouldScrollToId !== '') {
      const element = document.getElementById(props.shouldScrollToId);
      if (element) {
        element.scrollIntoView({ behavior: 'smooth' });
      }
    }
  });
  ....
  return (
   <div
      className="message-list overflow-scroll"
      id="scrollableDiv"
      style={{
        display: 'flex',
        flexDirection: 'column-reverse'
      }}>
      <InfiniteScroll
          dataLength={props.data.length}
          hasMore={props.hasMoreData && !props.isLoadingScrollData}
          inverse={true}
          next={props.fetchScrollData}
          scrollableTarget="scrollableDiv"
          style={{ display: 'flex', flexDirection: 'column-reverse', perspective: '1px' }}
          loader={<></>}>
          <div>
           {props.data.map((d, index) => {
            // Here i am setting the id which the useEffect will use to do the scroll
            <div key={index} id={d.id} >div - #{message}</div>;
           })}
         </div>
        {props.isLoadingScrollData ? <Spinner></Spinner> : null}
      </InfiniteScroll>
    </div>

I had some problems with the loader, so I decided not use it. Instead I created my own Spinner Component and make it appear or disappear according to the prop which shows if we are still fetching data from the scroll.

Upvotes: 0

niorad
niorad

Reputation: 1370

If you have a reference of the new element that is added, you can use element.scrollIntoView() to make sure it's visible after rendering.

https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView

But: In chat-rooms, this may not always be the right thing to do, as the user may have scrolled up to copy/paste something, and losing the position would be annoying. So check if the chat was already on the bottom before you scroll via JS.

You can take a look at how twitch-chat works to get a good example. The user gets an info if there are new chat messages.

Upvotes: 1

Related Questions