javascriptreactjsreact-hooksasync-await

How to properly wait for API response before accessing data in React tree component


I'm building a React application with a tree component. When a user clicks on a root node, I want to automatically display the first child item of that node.

The Problem

When users click too quickly on a node, they may click before the API has finished fetching the child items. This creates a race condition where I try to access tree[0].items[0] but tree[0].items is still empty.

My Current Solution

I've implemented a polling approach to wait for the data:

const getFirstItemAsync = useCallback(async () => {
  while (isEmpty(tree?.[0]?.items)) {
    await new Promise((resolve) => setTimeout(resolve, 1000));
  }
  return tree?.[0]?.items?.[0];
}, [tree]);

Here's how I'm fetching the tree data:

const loadItemsFn = useCallback(
  (item, targetId, isExpand = true) => {
    if (loadItems) {
      loadItems(params).then(
        (data) => {
          setTree(data)
        }
      );
    }
  },
  [loadItems, tree]
);

What I'm Looking For

I believe there should be a cleaner way to track when the API call completes instead of polling with setTimeout. How can I refactor this to properly wait for the API response before trying to access the first item?


Solution

  • You could do something like the following

    const [isTreeLoading, setIsTreeLoading] = useState(false);
    const loadItemsFn = useCallback(
        (item: A, targetId: string, isExpand: boolean = true) => {
          if (loadItems) {
            setIsTreeLoading(true);
            loadItems(params).then(
              (data: ITreePayload<ITreeItem>) => {
                setTree(data)
              }
            ).finally(()=>{
              setIsTreeLoading(false);
            })
          }
        },
        [loadItems, tree ]
      );
    
    // now you can use the isTreeLoading variable whenever you want to 
    

    This is a initial approach to this issue, just to give the general idea. In reality you should have to check/handle for errors of the request etc.

    There are libraries that can provide much of this out of the box, like TanStack query (previously known as react-query)