lukasbach / react-complex-tree

Unopinionated Accessible Tree Component with Multi-Select and Drag-And-Drop
https://rct.lukasbach.com
MIT License
944 stars 74 forks source link

Uncontrolled tree state is not changed #244

Closed bcburak closed 1 year ago

bcburak commented 1 year ago

I'm trying to create a new node with the same level of tree structure. In React side it's all working with useState. I expect the existing tree will re-render and showed the updated "treeItems" state, however I can not see the tree nodes with updated tree state. How should I add a new node with a specific properties I define?

Here is my tree and state code;

`const actualTree: ExactTree = {
  root: {
    index: "root",
    canMove: true,
    isFolder: true,
    children: ["Projects"],
    data: "root",
    canRename: true,
    countIndex: 2,
  },
  Projects: {
    index: "Projects",
    canMove: true,
    isFolder: true,
    children: ["GarbageCollector"],
    data: "Projects",
    canRename: true,
    countIndex: 2,
  },
  GarbageCollector: {
    index: "GarbageCollector",
    canMove: true,
    isFolder: true,
    children: ["Mas", "Blueberry"],
    data: "GarbageCollector",
    canRename: true,
    countIndex: 2,
  },
  Mas: {
    index: "Mas",
    canMove: true,
    isFolder: false,
    data: "Mas",
    canRename: true,
    countIndex: 2,
  },
  Blueberry: {
    index: "Blueberry",
    canMove: true,
    isFolder: false,
    data: "Blueberry",
    canRename: true,
    countIndex: 3,
  },
};`
` <UncontrolledTreeEnvironment<string>
            canDragAndDrop
            canDropOnFolder
            canReorderItems
            dataProvider={
              new StaticTreeDataProvider(
                treeItems as ExactTree,
                (item, data) => ({
                  ...item,
                  data,
                })
              )
            }
            getItemTitle={(item) => item.data}
            onFocusItem={(item) => setFocusedItem(item)}
            canRename={true}
            defaultInteractionMode={{
              mode: "custom",
              createInteractiveElementProps: (
                item,
                treeId,
                actions,
                renderFlags
              ) => ({
                onDoubleClick: (e) => {
                  e.preventDefault();
                  onTreeItemDoubleClick(e, item);
                },
                tabIndex: !renderFlags.isRenaming
                  ? renderFlags.isFocused
                    ? 0
                    : -1
                  : undefined,
              }),
            }}
            viewState={{
              "tree-1": {
                expandedItems: [],
              },
            }}
            {...bpRenderers}
          >
            <Tree treeId="tree-1" rootItem="root" treeLabel="Projects tree" />
          </UncontrolledTreeEnvironment>`
const addNewTreeNode = (nodeName: any) => {
       nodeName = "Environment";

    const treeNode = {
      index: nodeName,
      canMove: true,
      isFolder: false,
      data: nodeName,
      canRename: true,
      countIndex: 2,
    };

    actualTree[nodeName] = treeNode;
    // const jsonArray = JSON.stringify([actualTree]);

    setTreeItems(actualTree);
    console.log("setted", treeItems);
  };
lukasbach commented 1 year ago

If you want to react to tree state changes, I suggest you implement a custom TreeDataProvider rather than using StaticTreeDataProvider. There you can implement logic that reacts to change events. If you need more control, you can also use a controlled environment rather than an uncontrolled environment, then you get complete control over your data.

mikebridge commented 7 months ago

@lukasbach It's not clear to me how to rerender the tree with fresh data from the TreeDataProvider interface---if I add a new node, delete a node, or want to refresh the data completely, do I need to emit an event somehow? It's not clear to me how my custom TreeDataProvider needs to communicate changes to the tree.

mikebridge commented 7 months ago

Yeah, I can update the data in the provider but I can't see how to get the tree to see it (except when renaming).

I tried setting the new data then calling this.onDidChangeTreeDataEmitter.emit(Object.keys(this.data.items)) thinking that would maybe refresh the entire tree and that maybe something was listening. That did nothing.

I also tried recreating the provider entirely. It doesn't seem to see the change. I'm sure there's something easy here but I can't figure it out.

mikebridge commented 7 months ago

Also did not work:

lukasbach commented 7 months ago

@mikebridge are you using a custom data provider, or static tree data provider? With the latter, calling onDidChangeTreeDataEmitter with affected, or all, tree ids should work. In case of a custom provider, you need to make sure to propagate the changes to all listeners registered through onDidChangeTreeData.

I just expanded the docs on static data providers and on custom data providers. Maybe you can find something helpful there.

If not, can you open an issue for your problem and share your code there? I would need to see the implementation to see what's going wrong.

mikebridge commented 7 months ago

@mikebridge are you using a custom data provider, or static tree data provider? With the latter, calling onDidChangeTreeDataEmitter with affected, or all, tree ids should work. In case of a custom provider, you need to make sure to propagate the changes to all listeners registered through onDidChangeTreeData.

I just expanded the docs on static data providers and on custom data providers. Maybe you can find something helpful there.

If not, can you open an issue for your problem and share your code there? I would need to see the implementation to see what's going wrong.

Thanks! I was able to get this to work based on your updated examples---the issue was mainly to do with knowing what events to emit. I will clean my code up and post it when I have a few minutes.

(I was working with a custom provider which was basically the static data provider.)

lukasbach commented 7 months ago

Happy to hear! Yes the docs were lacking before on that topic, I hope now it's clearer.