lukasbach/react-complex-tree

Tree rerender with Custom TreeDataProvider when data state changes externally

Opened this issue · 1 comments

I am trying to re render the tree when the filesystem data is changed by another component and passed to the tree in props. I have implemented this Custom TreeDataProvider:

import {
  Disposable,
  TreeDataProvider,
  TreeItem,
  TreeItemIndex,
} from "react-complex-tree";
import { DirectoryWithLoadedChildren } from "../../types/fs";
import { EventEmitter } from "react-complex-tree/lib/esm/EventEmitter";

export class FSTreeDataProvider
  implements TreeDataProvider<DirectoryWithLoadedChildren>
{
  private data: Record<string, DirectoryWithLoadedChildren>;
  public treeChangeListeners: ((changedItemIds: TreeItemIndex[]) => void)[] =
    [];

  public readonly onDidChangeTreeDataEmitter = new EventEmitter<
    TreeItemIndex[]
  >();

  constructor(items: DirectoryWithLoadedChildren) {
    const newItems: Record<string, DirectoryWithLoadedChildren> = {};
    const addItems = (item: DirectoryWithLoadedChildren) => {
      newItems[item._id!] = item;
      if (item.loadedChildren) {
        item.loadedChildren.forEach(addItems);
      }
    };
    addItems(items);
    this.data = newItems;
  }

  public async getTreeItem(
    itemId: TreeItemIndex
  ): Promise<TreeItem<DirectoryWithLoadedChildren>> {
    const item = this.data[itemId as string];
    return {
      index: item._id!,
      data: item,
      isFolder: item.type === "directory",
      children: item.loadedChildren?.map((child) => child._id!) || [],
    };
  }

  public onDidChangeTreeData(
    listener: (changedItemIds: TreeItemIndex[]) => void
  ): Disposable {
    this.treeChangeListeners.push(listener);
    return {
      dispose: () =>
        this.treeChangeListeners.splice(
          this.treeChangeListeners.indexOf(listener),
          1
        ),
    };
  }
}

Here's the relevant code in the component. I am trying to emit an event in the useEffect.

const FSTreeSidebar = ({ fileSystem, onNodeClick }: FSTreeSidebar) => {
  const dataProvider = new FSTreeDataProvider(fileSystem);

  useEffect(() => {
    // console.log("File system updated:", fileSystem);
    dataProvider.onDidChangeTreeDataEmitter.emit([fileSystem._id!]);
  }, [fileSystem]);

  return (
    <>
      <UncontrolledTreeEnvironment<DirectoryWithLoadedChildren>
        dataProvider={dataProvider}
        getItemTitle={(item) => item.data.path.split("/").pop()!}
        viewState={{}}
      >
        <Tree
          treeId="tree-1"
          rootItem={fileSystem._id!}
          treeLabel="File System Tree"
        />
      </UncontrolledTreeEnvironment>
    </>
  );
};

How do I get the tree to rerender when the fileSystem data changes?

Thanks.

    dataProvider.onDidChangeTreeDataEmitter.emit([fileSystem._id!]);

You have to tell the library the "indexes" that changed, including "root".

I would say to purposefully put all IDs in as an initial POC, then slowly cut to get a handle of how to control the rendering.