Main idea of virtualized list to render it as a list.
If you pass down tree like structure and render it like in your code sample
<List
....
rowCount={data.length}
/>
You don't change rowCount value and keep expanded state in your Node component.
const Node = ({ data, listRef, depth }) => {
const [isExpanded, setIsExpanded] = React.useState(false);
But then you scroll out of screen your Node element will be destroyed and recreated then you return.
You need to keep your selections outside of Node element.
like
// [key]: value structure there key is id of element and value [true, false].
const rootObject = {[elementId]: true};
const App = () => {
const [visibleNodes, setVisibleNodes] = useState(rootObject)
....
<List
...
rowRenderer={({ index, style, key }) => {
return (
<Node
setVisibleNodes={setVisibleNodes}
visibleNodes={visibleNodes}
style={style}
key={key}
data={data[index]}
listRef={ref}
depth={1}
/>
);
}}
rowCount={data.length}
width={width}
/>
And in Node
const Node = ({ data, listRef, depth, setVisibleNodes, visibleNodes }) => {
const isExpanded = visibleNodes[data.id];
const handleClick = (e) => {
if (data.children.length === 0) return;
e.stopPropagation();
setVisibleNodes({...visibleNodes, [data.id]: !!isExpanded});
listRef.current.recomputeRowHeights();
listRef.current.forceUpdate();
};
return (
<div onClick={handleClick}>
{data.children.length ? (isExpanded ? "[-]" : "[+]") : ""} {data.name}
{isExpanded && (
<div style={{ marginLeft: depth * 15 }}>
{data.children.map((child, index) => (
<Node
key={index}
data={child}
listRef={listRef}
depth={depth + 1}
/>
))}
</div>
)}
</div>
);
};
I think it works)
But it's better to do such things like real list and make tree hierarchy just visually. By that way you'll use Virtualisation List as it was purposed by creators)