headless-tree icon indicating copy to clipboard operation
headless-tree copied to clipboard

How to refresh the data without fully managing the tree view state

Open softmarshmallow opened this issue 7 months ago • 9 comments

I am adopting headless tree to my visual editor.

It has move up / down feature, which simply is reordering children: ["item-1", "item-2"] to children: ["item-2", "item-1"]

Image

The view does not reflect this, how do I ...

  • option: set a refresh key to force re-render entire tree
  • option: let the tree know that children has changed
  • option: let the tree know item with id "x" has changed, re-draw with its property, including its children.

softmarshmallow avatar May 21 '25 09:05 softmarshmallow

Hi! HT exposes functionality to allow consumers to let HT know if the external data source has changed, and it will update its data accordingly, which is documented here: https://headless-tree.lukasbach.com/recipe/external-state-updates/

Note that HT only triggers rerenders by updating any of the HT state variables, so if the change to the data doesn't change any HT state information, HT will not trigger a React rerender by itself, but since it just provides data for a React component to render, any other update within the component can also be used to trigger a rerender manually.

lukasbach avatar May 21 '25 23:05 lukasbach

I understand the reasons behind it. overall (IMHO ), It does not feel like a react library, but a wrapper around a extenal one. (which it actually is though).

The principle behind state management should be single (do not mix the concepts).

  1. Let user manage state
  2. Let us manage state -> we'll let you know if changed
  3. Let user manage state + we'll give you a reducer (utility / plugin / what ever its called) to manage it easier.

It's been only 3 days playing around with this module, I still got lot to learn, true.

With my usecase, to leverage all feature, I ended up doing below, where document being the entire app state. (which is terrible)

  useEffect(() => {
    tree.rebuildTree();
  }, [document]);

My app already does abstracts the tree structure with id only reference, but there is no reliable way to let the HT know what its dealing with. unless rebuilding it entirely.

Since it started with a great design where we don't pass the entire tree, but with a fetcher (getItem / getChildren) It be nice to have a interface to pass a refresh key or atleast a while reference tree as a dependency.

I might be just talking garbage, feel free to completely ignore ; )

softmarshmallow avatar May 23 '25 07:05 softmarshmallow

You're not talking garbage, I'm happy for the feedback you provide! :)

I'll have to think a bit more about this. I guess the main issue/confusion comes from the fact that HT is mostly responsive to state updates, but the tree data is not actually considered as state, but external data fetchers. The state that triggers rerenders for HT is only expanded items, focused items, renaming/drag state etc. The goal of the HT data management is what you mentioned in (3), the user manages the state, and passes a reducer/custom interface to let HT know how to navigate the data source, but maybe there needs to be a better way to model the event flow in the other direction, from outside to the inside of HT.

lukasbach avatar May 24 '25 21:05 lukasbach

Hey, I'm wondering if tree.rebuildTree() is called every time any item is mutated, when the tree has a huge amount of items, will it cause serious performance issues?

Is it possible to just rebuild a specific item(s) and its children?

NotYoojun avatar Jun 11 '25 09:06 NotYoojun

Hi @NotYoojun! I had plans for rebuilding only subtrees, but found that the tree rebuilding doesn't really add to much performance overhead, while the logic of handling subtree mutations would add a lot of complexity, so I've pushed that back for now.

I found that HT handles trees with virtualization with up to a few 100k items well, you can play around with a large sample tree at https://headless-tree.lukasbach.com/storybook/react/?path=/story/react-scalability-basic-virtualization--basic-virtualization&args=openLevels:4

lukasbach avatar Jun 11 '25 09:06 lukasbach

I found that HT handles trees with virtualization with up to a few 100k items well

What about frequently updating item data in a large sample tree? For example, a tree has 100k items but a few items (like 8 of them) keeps updating (like changing its name every 2 seconds, adding or removing a few children) while most of items (other 99992 items) stay still.

NotYoojun avatar Jun 11 '25 10:06 NotYoojun

What about frequently updating item data in a large sample tree? For example, a tree has 100k items but a few items (like 8 of them) keeps updating (like changing its name every 2 seconds) while most of items (other 99992 items) stay still

Depends a bit on the specifics, but rebuildTree regards only the structure of the tree, i.e. children-array, level, open/closed state of items, the name of an item is not relevant for this so changes to item names can be done without triggering rebuildTree. So this can be implemented in a way where tree mutations aren't created at all, the tree would still have to re-render for every change, but if the tree is virtualized (and if necessary, items are memoized), that should be fine.

lukasbach avatar Jun 11 '25 10:06 lukasbach

Okay, I'm starting to get your idea.

For tree-structured changes, rebuildTree should be used, and each time the whole tree will be updated; Changes of the item itself, should be handled in the Item component instead of the tree. Is that correct?

NotYoojun avatar Jun 11 '25 10:06 NotYoojun

Pretty much, yes. For structure-related changes that are triggered from within the tree, HT automatically invokes rebuildTree internally, so for example if you open/close trees by clicking them, you just need to call that handler if you mutate the structure from the outside.

What this method does btw is updating the internal representation of a flattened tree with some structure-related metadata assigned to each item, so HT can more efficiently render the tree as list and handle drag events that go through what the user sees as a flattened list of tree items. All other information related to items is just generic item payload that you can pretty much handle yourself.

lukasbach avatar Jun 11 '25 11:06 lukasbach