react-ui-tree icon indicating copy to clipboard operation
react-ui-tree copied to clipboard

Lazy loading

Open edgesoft opened this issue 8 years ago • 14 comments

I have a very large tree that I won't be able to load on initialization. Would it be possible to do lazy loading? A user clicks on a node and fetches data from server and adds it to the tree. We would need some way of telling the node that it has children but it should be loaded from the server.

edgesoft avatar Oct 20 '15 10:10 edgesoft

How large is the tree?

wangzuo avatar Oct 21 '15 07:10 wangzuo

More than 10.000 nodes ( Not for every user. Some has just 100 ). All won't expand every time a user works in the tree.

edgesoft avatar Oct 21 '15 12:10 edgesoft

Are you not able to do that in renderNode? You could update the tree when a node is selected.

dannav avatar Oct 21 '15 13:10 dannav

Let's say I want to move a node with many children to another nodes child. I need to expand the node on hover and then fetch data from server. And then be able to hover yet another node that was just fetched and fetch until the user finds which node he/she wants to move to. I need to refresh on hover and expand lazily. I don't know how to accomplish that right now.

edgesoft avatar Oct 21 '15 13:10 edgesoft

@wangzuo Proposing to add lazy boolean to the tree. If the tree is lazy then we should check index.hasChildren and not just index.children && index.children.length. An onBeforeOpenNode event should be called in toggleCollapse to allow fetching data from server before we expand.

{ module: 'parent', leaf: false, hasChildren: true }

edgesoft avatar Oct 22 '15 09:10 edgesoft

Something like this so far. This will enable lazyloading per node.

modules

{
   module: 'test',
   leaf: false,
   hasChildren: true,
   collapsed: true,
   lazy: true
}

react-ui-tree.js

 toggleCollapse(nodeId) {
    var tree = this.state.tree;
    var index = tree.getIndex(nodeId);
    var node = index.node;
    if( this.props.onLazyloadNode && node.lazy && node.hasChildren ){
      node.lazy = false;
      this.props.onLazyloadNode(node, function(nodes){
          nodes.map( newNode => {
             tree.append(newNode,nodeId);
          } );
         this.makeToggleUpdate(node);
      }.bind(this));
    }else{
      this.makeToggleUpdate(node);
    }
  },

  makeToggleUpdate(node){
    var tree = this.state.tree;
    node.collapsed = !node.collapsed;
    tree.updateNodesPosition();

    this.setState({
      tree: tree
    });

    this.change(tree);
  }

node.js. Updated node.hasChildren

renderCollapse() {
    var index = this.props.index;
    var node = index.node;
    if(node.hasChildren || (index.children && index.children.length)) {
      var collapsed = index.node.collapsed;

      return (
        <span
          className={cx('collapse', collapsed ? 'caret-right' : 'caret-down')}
          onMouseDown={function(e) {e.stopPropagation()}}
          onClick={this.handleCollapse}>
        </span>
      );
    }

    return null;
  },

exampel app.js


  <Tree
       paddingLeft={20}
       tree={this.state.tree}
       onChange={this.handleChange}
       isNodeCollapsed={this.isNodeCollapsed}
       renderNode={this.renderNode}
       shouldRenderRootNode={true}
       onLazyloadNode={this.onLazyloadNode}
   />


  onLazyloadNode(node,cb){
    console.log( "Before expand" );
    setTimeout(function(){
      var nodes  = [
        {module: 'mathias', leaf: false, collapsed: true, lazy: true,hasChildren : true}
      ];
      cb( nodes );
      console.log( "After expand" );
    },2000);

  },

edgesoft avatar Oct 22 '15 10:10 edgesoft

This seems like a good improvement that will become more necessary as the amount of nodes increase. I have a project that has around 5000 nodes and it can get quite slow.

Should the suggested changes be in a pull request?

tobyndockerill avatar Oct 26 '15 05:10 tobyndockerill

Thanks! will try to add this feature.

wangzuo avatar Oct 29 '15 06:10 wangzuo

@wangzuo I didn't commit this yet but accepted the pull request from @tobyndockerill because there is still a problem to solve. When clicking the expand arrow multiple times we should have a behaviour. What should happen? Right now we set the lazy to false so that we don't fetch data from server again. But the toggle is slow an feels unresponsive. Should we lock the node currently lazyloading to prevent toogle when loading? Have a spinner? any suggestions?

edgesoft avatar Oct 29 '15 06:10 edgesoft

The best suggestion so far is to lock click on the arrow and show a svg spinner instead of the arrow. Is this a good solution? @wangzuo @tobyndockerill

edgesoft avatar Oct 29 '15 07:10 edgesoft

I agree. A loading indicator would go a long way to show the user that something is happening behind the scenes.

tobyndockerill avatar Oct 29 '15 22:10 tobyndockerill

I am also super interested in this, drag and drop becomes extremely slow even in a moderately sized tree

rohan-deshpande avatar Nov 06 '15 00:11 rohan-deshpande

I'm interested in this too, any chance there's going to be a PR for this soon?

mofadlalla avatar Apr 26 '16 13:04 mofadlalla

I'd love to see this feature implemented as well. It would help immensely, and it is a very nice thing to have anyway.

wizzardich avatar Nov 11 '17 12:11 wizzardich