Closed sumanthyedoti closed 3 weeks ago
I had this same issue. I'm using a table with table rows in my example, but it would seem the problem has the same cause.
My solution was to add a span wrapping the content of the row, and have that be the ref to set the height based on.
I am also suffering from the same problem. Can you show your solution with an example?
@batu0b Sure.
I followed this example: https://codesandbox.io/s/react-chat-simulator-yg114?file=/src/ChatRoom/Room/Messages/Messages.js:569-1341
But I'll adjust it for my specific use case which was using a table:
function TableRow({ index, style }) {
const rowRef = useRef({});
useEffect(() => {
if (rowRef.current) {
setRowHeight(index, rowRef.current.clientHeight);
}
// eslint-disable-next-line
}, [rowRef]);
return (
<tr style={style}>
<td ref={rowRef}>
{messages[index].message}
</td>
</tr>
);
}
The TD element wasn't growing to the height of the content, so I ended up doing this:
function Row({ index, style }) {
const rowRef = useRef({});
useEffect(() => {
if (rowRef.current) {
setRowHeight(index, rowRef.current.clientHeight);
}
// eslint-disable-next-line
}, [rowRef]);
return (
<tr style={style}>
<td>
<span ref={rowRef}>
{messages[index].message}
</span>
</td>
</tr>
);
}
The SPAN element will be the height of the content, so we will set the ref there, and react-window should work properly.
@danmolitor
Thank you very much for your reply, now my code is working without errors
For me it worked if I passed the messages as an array like in this example but if I pass the messages as children and convert the children to an array it doesn't work. It defaults to the default value of 82. Here is my github repo:
// Modules
import React, { useEffect, useRef, FC, ReactNode, Children } from "react";
// Components
import { VariableSizeList as List } from "react-window";
import AutoSizer, { Size } from "react-virtualized-auto-sizer";
// Styles
import styles from "./Messages.css";
import { Box } from "@mui/material";
// Define the shape of a message
type Message = {
sender: string;
message: string;
};
// Define the props for the Messages component
interface MessagesProps {
messages?: Message[];
children?: ReactNode;
}
// The Messages functional component with types for props
const Messages: FC<MessagesProps> = ({ children, messages }) => {
// References with types
const listRef = useRef<List>(null);
const rowHeights = useRef<{ [key: number]: number }>({});
// Convert children to an array so we can access by index.
const childrenArray = Children.toArray(children);
useEffect(() => {
if (childrenArray.length > 0) {
scrollToBottom();
}
// eslint-disable-next-line
}, [childrenArray]);
function getRowHeight(index: number): number {
return rowHeights.current[index] + 8 || 82;
}
interface RowProps {
index: number;
style: React.CSSProperties;
}
const Row: FC<RowProps> = ({ index, style }) => {
const rowRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (rowRef.current) {
setRowHeight(index, rowRef.current.clientHeight);
}
// eslint-disable-next-line
}, [rowRef]);
return (
<div style={style} ref={rowRef}>
{childrenArray[index]}
</div>
);
};
function setRowHeight(index: number, size: number): void {
if (listRef.current) {
listRef.current.resetAfterIndex(0);
rowHeights.current = { ...rowHeights.current, [index]: size };
}
}
function scrollToBottom(): void {
if (listRef.current) {
listRef.current.scrollToItem(childrenArray.length, "end");
}
}
return (
<Box
sx={{ overflowY: "scroll", flexGrow: 1, p: 1 }}
data-testid="message-list"
>
<AutoSizer style={styles.messagesContainer as React.CSSProperties}>
{({ height, width }: Size) => (
<List
className="List"
height={height}
itemCount={childrenArray.length}
itemSize={getRowHeight}
ref={listRef}
width={width}
>
{Row}
</List>
)}
</AutoSizer>
</Box>
);
};
export default Messages;
I used react-virtuoso instead of react-window and it woks with children array:
import React, { useCallback } from "react";
import { Virtuoso } from "react-virtuoso";
import { Box } from "@mui/material";
interface MessageListProps {
children: React.ReactNode;
}
const MessagesList: React.FC<MessageListProps> = ({ children }) => {
// Convert ReactNode children to an array of elements.
const childrenArray = React.Children.toArray(children);
const onFollowOutputHandler = useCallback((atBottom: any) => {
if (atBottom || true) {
return "auto";
} else {
return false;
}
}, []);
return (
<Box sx={{ flexGrow: 1, p: 1}} data-testid="message-list">
<Virtuoso
data={childrenArray}
followOutput={onFollowOutputHandler}
style={{ height: "100%", width: "100%" }}
totalCount={childrenArray.length}
initialTopMostItemIndex={childrenArray.length - 1} // Set initialTopMostItemIndex to the last item
itemContent={(index) => (
<Box sx={{ pb: 1 }}>{childrenArray[index]}</Box>
)}
/>
</Box>
);
};
export default MessagesList;
I followed this example. It is working fine. But there is a problem with my implementation and I could not see what I am missing. I am getting just the default height
|| 60
even when Iconsole.log
rowRef.current
inMeesageRow
when setting the height, it is the default60
why is this? Where is the mistake, please? Thnaks.Code
MessageRow