-
-
Notifications
You must be signed in to change notification settings - Fork 323
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to implement reverse list like 'flex-direction: column-revserse'? #27
Comments
I would be interested in hearing if anyone has figured out an implementation using this library as a chat app. I can't seem to find a virtual-scroll react library that implements such a feature without some hacking, which seems odd since so many apps implement a chat style app! Any suggestions are welcome... |
Chiming in to say that as @uncvrd said, I too never found a way to get a chat app working with virtualized list :( |
Seem like not even react-windows has a solution for it
And most likely will not happen :( |
This issue has been automatically closed due to inactivity. If this issue needs more attention, please comment or open a new issue with updated information. Thanks! |
I solved this. Here is a custom version of react-virtual in a sandbox showing reverse scroll using I wasn't able to figure out how to import my branch into the sandbox so just hardcoded my version therein, see the The critical details for the end user are to add I've ensured this works well for infinite scroll and with Again, see the sandbox. @tannerlinsley if this looks right to you Im happy to provide a PR. I did not add tests as, well, time... Blessings. To my knowledge this may be the first working version of virtual lists that actually works with column-reverse for any of the projects. |
Oh, I should add, there is one bit of styling in there that was specific to a chat use case (e.g.; if there is less content than can fill the parent that content sits at the top until the content fills the parent and then sticks to the bottom so content loaded above goes above the fold). This can be adjusted by changing the CSS in that file (remove |
I tried it out on the bigest reverse scroll killer - iOS, and it's got a glitch :( It starts off in the wrong place (not at the bottom) and nothing is painted to the screen. Screen.Recording.2022-10-13.at.3.15.17.PM.mov |
Reverse infinite scroll list example - https://codesandbox.io/p/sandbox/immutable-silence-76pwko?welcome=true It's transforming the list and items (scaleY -1) and inverting the mousewheel event. Screen.Recording.2023-05-04.at.5.26.17.PM.mov@tannerlinsley Happy to send an example PR if this looks good to you! |
Would love to see this feature merged into the repo! |
any word on this @tannerlinsley? |
would love to have this reverse scroll with dynamic sizes |
Please an example of reverse with dynamic |
Imho react-virtual should have full column-reverse support, it's basically the first thing devs are looking for when doing a chat-like app, and since AI stackoverflow has seen a huge increase in devs asking for a virtualization library which has support for it. I'm currently using the sample from @piecyk (props to him) which basically inverts the virtual items, it works, but it is still not as good as using column-reverse. @tannerlinsley |
I need this bad for dynamic lists, do I full send and try to make a PR today 😅 |
I hope this helps someone this works with dynamic images in my case, I am using import { useVirtualizer } from "@tanstack/react-virtual";
import { useInfiniteQuery } from "@tanstack/react-query";
import { useEffect, useRef } from "react";
import Image from "next/image";
const reverseScroll = (e) => {
e.preventDefault();
e.currentTarget.scrollTop -= e.deltaY;
};
export const PaymentMessagesChat = () => {
const parentRef = useRef(null);
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteQuery({
queryKey: ['paymentMessages'],
queryFn: fetchPaymentMessages,
getNextPageParam: (lastPage) => lastPage.nextCursor,
});
const messages = data?.pages.flatMap((page) => page.messages) || [];
const rowVirtualizer = useVirtualizer({
count: hasNextPage ? messages.length + 1 : messages.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 100,
});
useEffect(() => {
const lastItem = rowVirtualizer.getVirtualItems().at(-1);
if (lastItem?.index >= messages.length - 1 && hasNextPage && !isFetchingNextPage) {
fetchNextPage();
}
}, [rowVirtualizer.getVirtualItems(), messages, hasNextPage, isFetchingNextPage, fetchNextPage]);
useEffect(() => {
const current = parentRef.current;
current?.addEventListener("wheel", reverseScroll, { passive: false });
return () => void current?.removeEventListener("wheel", reverseScroll);
}, []);
return (
<div ref={parentRef} className="relative my-4 h-[27rem] scale-y-[-1] overflow-y-auto">
<div style={{ height: `${rowVirtualizer.getTotalSize()}px` }}>
{rowVirtualizer.getVirtualItems().map((virtualRow) => {
const message = messages[virtualRow.index];
const isLoaderRow = virtualRow.index > messages.length - 1;
return (
<div
key={virtualRow.key}
data-index={virtualRow.index}
ref={rowVirtualizer.measureElement}
style={{
transform: `translateY(${virtualRow.start}px) scaleY(-1)`,
position: 'absolute',
top: 0,
left: 0,
width: '100%',
}}
className="flex gap-4 p-2"
>
{!isLoaderRow && <MessageItem message={message} />}
</div>
);
})}
</div>
</div>
);
};
const MessageItem = ({ message }) => (
<>
<UserAvatar user={message.user} />
<div className="max-w-[80%] rounded-lg p-3 bg-primary text-primary-foreground">
<p>{message.message}</p>
{message.image && (
<Image
src={message.image}
alt={message.message}
className="object-contain my-2 max-w-full"
width={0}
height={0}
sizes="100vw"
style={{ width: '100%', height: 'auto' }}
/>
)}
<div className="flex items-center space-x-5 text-sm opacity-70">
<span>@{message.user.username}</span>
<span>{new Date(message.createdAt).toLocaleString()}</span>
</div>
</div>
</>
);
const UserAvatar = ({ user }) => (
<div className="h-8 w-8 rounded-full bg-gray-300 flex items-center justify-center">
{user.avatar ? (
<img src={user.avatar} alt={user.username} className="h-full w-full rounded-full" />
) : (
<span>{user.username.slice(0, 2).toUpperCase()}</span>
)}
</div>
); |
@Zerebokep why not as good? |
@piecyk my main issue is that it is slower and you have to adjust the scroll position on multiple occurrences (e.g. scroll to bottom on load, scroll to bottom on chat submit, when an element expands below the list or list item), which also causes the scrollbar to show up (I know there are workaround). From a developer experience when switching from column-reverse to an inversed virtual list it really feels that you have implement a dozen workarounds to get to the same ux you had with column-reverse. Please don't get me wrong, I really love react-virtual, imo it is the best virtualization library out there (all our frontend teams are using it), but something like @bradley suggested would be the killer feature which many devs (+ myself) out there are currently looking for. |
+1 ^ |
how to implement reserve list, like chat app?
The text was updated successfully, but these errors were encountered: