Reputation: 115
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
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
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