GithubHelp home page GithubHelp logo

Comments (5)

lukasbach avatar lukasbach commented on May 22, 2024 1

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.

lukasbach avatar lukasbach commented on May 22, 2024 1

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.

janmejayspurohit avatar janmejayspurohit commented on May 22, 2024

+1

from react-complex-tree.

C7X avatar C7X commented on May 22, 2024

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.

rozzzly avatar rozzzly commented on May 22, 2024

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 this

    export 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 copypasted StaticTreeDataProvider but changed the onChangeItemChildren method to keep parent up to date

    public 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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.