Comments (5)
Hi, thanks for reporting! Just as an heads up, I've looked into it, and it is not quite as trivial to fix; since checking whether someone is dropping an item into itself is not a trivial operation (can be a high depth through which is being dropped, and arbitrarily many items can be dragged at once, so the check is required for every dragged item), this will require some calculation. And, currently, the drop check happens on the drag event, not the drop event (so that the library can already prevent dropping on not allowed items), so this might not scale well for bigger trees. I'm currently working on improving the overall performance of the library, and then plan to only do the check during the initial drag event, hopefully in a way that only requires linear effort. Until then, a workaround similar to what @impactvelocity suggested is probably the easiest way to go. I'll keep you updated on the progress!
from react-complex-tree.
Hi @C7X! When working with lists with thousands of entries, list virtualization starts to become inevitable. This is something I want to support with react-complex-tree in the future, but there is still a bigger road ahead for that and that will not come with the performance fixes I am working on right now, sorry. So for now, I unfortunately have to say that other libraries might be a better starting point until rct supports virtualization.
from react-complex-tree.
+1
from react-complex-tree.
Hi @lukasbach, great work so far! I’m so glad to hear that you’re working on performance right now. We’re currently trying to use your library for a drag and drop tree that has thousands of nodes and we’re finding that it slows down to the point of not being usable when we try to drag and drop something. Will your performance upgrades make it possible for us to use this library for trees that have thousands of nodes?
from react-complex-tree.
I was able to solve this quite easily, I'm not sure about performance but if I recall my time complexity notation correctly, it should be O(n)
. I wouldn't imagine this would be a performance hit unless you had 1k+ leaf nodes individually selected (not their parents)
Here's my approach in brief:
-
my items double linked so they maintain the ID of their parent (if any) as well as the IDs of any children and my typedef looks like this:
interface Item { _id: string; name: string; parent?: string; // if no parent, Item is the root node children: string[] }
-
I prepare them for
react-complex-tree
like thisexport interface TreeListProps { initialItems: Item[] } export const TreeList: React:FC<TreeListProps> = ({ initialItems ]) => { const { treeItems, rootID } = useMemo(() => { let rootID = ''; const result: Record<string, TreeItem<any>> = {}; for (const item of initialItems) { if (!item.parent) rootID = item._id; result[item[idKey]] = { data: { ...item }, index: item._id, children: item.children, hasChildren: item.children.length > 0, canMove: true }; } if (rootID === '') throw new Error('could not find root item!') return { treeItems: result, rootID }; }, []); // ... rest of component };
-
Next I created a custom
TreeDataProvider
. I just copypastedStaticTreeDataProvider
but changed theonChangeItemChildren
method to keepparent
up to datepublic async onChangeItemChildren(parentID: TreeItemIndex, nChildIDs: TreeItemIndex[]): Promise<void> { // update parent node const parent = this.data.items[parentID]; parent.children = nChildIDs; if (nChildIDs.length) { parent.hasChildren = false; } else { parent.hasChildren = true; } parent.data.children = nChildIDs; // re-parent child nodes for (const childID of nChildIDs) { const child = this.data.items[childID]; if (child.data.parent !== parentID) { // this child's parent has changed console.log( `${child.data.name} changed from [${this.data.items[child.data.parent].data.name}] => [${parent.data.name}]` ); child.data.parent = parentID; } } this.onDidChangeTreeDataEmitter.emit([parentID]); }
-
the data provider is initialized like so
const myCustomDataProvider = useMemo(() => ( new CustomTreeDataProvider(treeItems, (item, name) => ({ ...item, data: {...item.data, tagName: name }})); ), [ treeItems ]);
-
now that refs to parent are tracked, it is simple to check if user is attempting to drop a node into one of its descendants
<UncontrolledTreeEnvironment // ... other props dataProvider={myCustomDataProvider} canDropAt={(items, target) => { let parentID = target.parentItem; // check to ensure dragged node(s) is(are) not trying to drop into one of its(their) own descendants while (parentID) { // go up until we reach the root node if (items.some(item => item.index === parentID)) { console.log('can\'t drop a node into its own descendant'); return false; } else { parentID = treeItems[parentID].data.parent; } } return true; }} // .. more props > // ... </UncontrolledTreeEnvironment>
So that's all the logic I needed to achieve the desired behavior. At ~100 items, I haven't seen any performance issues. My attention span isn't long enough to setup a stress test. If some tries this and hits a wall at 1k selected items or something, I'd be interested to hear about it. I imagine debounce/throttle on canDropAt
would help against very large selections although you would probably need to have some logic to test parameters so the return value would always be valid for the given selection/target.
from react-complex-tree.
Related Issues (20)
- Request for `context.stopRenamingItem(item.index)` as counter for existing startRenamingItem function HOT 2
- Cannot read properties of undefined (reading 'index') HOT 3
- tree update HOT 1
- Drag custom files to the tree HOT 2
- Text selection on title don't work HOT 3
- Missing `activeTreeId` in `TreeEnvironmentRef<T,C>` type HOT 1
- Set state on unmounted component HOT 8
- Tree is missing `multiselect` prop HOT 2
- I want to print node contents. HOT 1
- Unable to update tree data using custom Data Provider HOT 3
- Collision with react-dnd HOT 2
- Search whole tree HOT 5
- Animation when node is opened and closed HOT 3
- `renderItem` and `renderItemTitle` rerenders causes performance hits HOT 1
- customize keyboard bindings HOT 1
- Custom search HOT 1
- Expand tree element without selecting it HOT 1
- Expose treeItem internals HOT 1
- Item sort order is not maintained when reparenting multiselections HOT 1
- can make overrideOldSelection is customizable when calling selectUpTo? HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from react-complex-tree.