Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazy loading pattern #135

Open
waliurjs opened this issue Apr 5, 2023 · 11 comments
Open

Lazy loading pattern #135

waliurjs opened this issue Apr 5, 2023 · 11 comments
Labels
demo documentation Improvements or additions to documentation

Comments

@waliurjs
Copy link

waliurjs commented Apr 5, 2023

Hi!
This is related to #127 Lazy loading children when parent is opened.

Can you guys please provide a pattern/pseudo code or some advice on how to implement it?

What I can gather from the docs is that I think I need to have a controlled component like this. But I have more questions.

Some concerns that I need advice on:

  • The data prop can be a huge nested JSON. So, manipulating mutating it will require some optimisations. I don't know what would be a good way to go about this.
  • Can the data prop be a flat array of objects referencing their parent by a id: string without being nested like children: []?
  • I saw some built in tree manipulating functions inside the react-arborist's codebase. It would be great if those functions can be re-used in controlled component too.

Desperately need some advice or some pseudo codes.

Thank you!

(Found your HackerNews comment)

@jameskerr
Copy link
Member

Yes, this is a good idea. I will create a demo with this as I have time.

@jameskerr jameskerr added the demo label May 10, 2023
@CHE1RON
Copy link

CHE1RON commented May 17, 2023

This would be super helpful, thanks @jameskerr!

@holy-dev
Copy link

Any update on this?

@holy-dev
Copy link

holy-dev commented Aug 1, 2023

@jameskerr brother, this is the last thing I need help with promise. Could you please guide me?

@jameskerr
Copy link
Member

Here's a PR that was just submitted shows an example of manually creating all the handlers for the Tree.

https://github.com/brimdata/react-arborist/pull/172/files

When a node is clicked, in the click handler, you'll make your api call, update the data, and the tree will re-render.

Good luck!

@holy-dev
Copy link

holy-dev commented Aug 2, 2023

@jameskerr the PR makes sense. I now know why my code wasn't working before. It'll be much easier to implement custom logics.

@Philipinho
Copy link

Philipinho commented Oct 5, 2023

I have been able to achieve lazy loading.

I created a custom hook from #172.
I replaced const [data, setData] = useState<T[]>([]) in the hook with jotai atom to be able to read and update the tree data globally.

export const treeDataAtom = atom<any>([]);
const [treeData, setTreeData] = useAtom(treeDataAtom);

In my Node component, the tree update is adapted from Ant design dynamic children .

When the node is clicked, the tree data is updated and persisted to the atom.
You can also load the children from an api.

function Node({ node, style, dragHandle }: NodeRendererProps<any>) {
  const [treeData, setTreeData] = useAtom(treeDataAtom);

  function updateTreeData(list, id, children) {
    return list.map((nodeItem) => {
      if (nodeItem.id === id) {
        return { ...nodeItem, children };
      }
      if (nodeItem.children) {
        return { ...nodeItem, children: updateTreeData(nodeItem.children, id, children) };
      }
      return nodeItem;
    });
  }
  function loadChildren({ id, children }) {
    return new Promise(resolve => {
      if (children && children.length > 0) {
        resolve();
        return;
      }

      setTimeout(() => {
        setData(origin =>
          updateTreeData(data, id, [
            {
               id: `${id}-0`,
              name: 'Child Node',
              children: [],
            },
            {
              id: `${id}-1`,
              name: 'Child Good',
              children: [],
            },
          ]),
        );
        resolve();
      }, 1000);
    });
  }

  return (
    <>
      <div
        style={style}
        className={clsx(styles.node, node.state)}
        ref={dragHandle}
        onClick={() => loadChildren(node)}
      >
        <PageArrow node={node} />

        <IconFileDescription size="18px" style={{ marginRight: '4px' }} />

        <span className={styles.text}>
          { node.data.name }
        </span>
      </div>
    </>
  );
}

I am not sure if I am doing it the right way but so far it works.

Working Demo:

lazy-loading-2023-10-05.at.15.28.48.mov

Related to #127

@CHE1RON
Copy link

CHE1RON commented Jan 16, 2024

@Philipinho Would you be willing to share your code? 🥇 I'm working on an implementation right now, and would love to be able to share & compare 😊

Cheers!

@Philipinho
Copy link

@CHE1RON It was more of a POC incase I would need it in my application (not yet needed). Unless I would spend some time to create a working demo. Lets see.

@stefantrinh1
Copy link

stefantrinh1 commented Feb 2, 2024

for those that don't want to do a time out. There is another way as the timeout isn't 100% reliable.

I tried to use a flag in the Node Component with useState, but it would reset the state when the tree re-rendered.

i first tried this but this caused problems with it opening every time the node re rendered and reset the local State Inside the TreeNode Component back to initial.

  useEffect(() => {
    if (children && children.length > 0) {
      node.open()
    }
  }, [children, children.length])

So I reached for the tree ref. and stored any nodes where I had finished lazy loading.


function Node({ node, style, dragHandle, setTree, tree }: any) {
...
  useEffect(() => {
    if (!tree?.store?.idsLoaded?.includes(_id) && node.isInternal) {
      node.open()
      tree.store.idsLoaded = [...(tree?.store?.idsLoaded ?? []), _id]
    }
  }, [node.isInternal, hasLoaded, tree])
 ...
 }

hope this helps anybody trying this

Screen.Recording.2024-02-01.at.5.21.21.PM.mov

@jameskerr jameskerr added the documentation Improvements or additions to documentation label Feb 7, 2024
@Philipinho
Copy link

@CHE1RON not sure if this is still relevant to you.

This is the live implementation in my software.
https://github.com/docmost/docmost/blob/df9110268c9059dd568afac8a74868e6a4815e30/apps/client/src/features/page/tree/components/space-tree.tsx#L132

Hook for mutating tree and syncing with backend: https://github.com/docmost/docmost/blob/df9110268c9059dd568afac8a74868e6a4815e30/apps/client/src/features/page/tree/hooks/use-tree-mutation.ts#L24

I am using Jotai to manage the tree data across components.

Happy to clear any doubts.

Screen.Recording.2024-04-17.at.21.20.44.mov

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
demo documentation Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

6 participants