Skip to main content

Async Data Loader

Data interface for asynchronous data sources

SourceView Source
TypesView Types
Importimport { asyncDataLoaderFeature } from "@headless-tree/core
Type DocumentationConfigurationStateTree InstanceItem Instance

The Async Data Loader is an alternative to the Sync Data Loader that allows you to define your data interface in an asynchronous manner. If you want to use it, you need to use the asyncDataLoaderFeature instead of the syncDataLoaderFeature in your tree config.


const tree = useTree<ItemPayload>({
rootItemId: "root",
getItemName: (item) => item.getItemData().name,
isItemFolder: () => item.getItemData().isFolder,
createLoadingItemData: () => "Loading...",
dataLoader: {
getItem: async (itemId) => await dataSources.getItem(itemId)
getChildren: async (itemId) => await dataSources.getChildren(itemId),
},
features: [ asyncDataLoaderFeature ],
});

You still hook the tree up to your data source with dataLoader, but can now use asynchronous methods for retrieving your tree data.

Fetching all children data at once

The dataloader also specifies an alternative interface for fetching the payload for all children of an item at once, in case it is more convenient to you that way. This can help to reduce the number of requests of your app when a folder is opened by a user.

const dataLoader = {
getItem: (itemId) => {
return wait(800).then(() => itemPayload);
},
getChildrenWithData: (itemId) => {
return wait(800).then(() => [
{ id: `child-1`, data: child1Payload },
{ id: `child-2`, data: child2Payload },
{ id: `child-3`, data: child3Payload },
]);
},
};

Loading Items

The Async Data Loader provides functionality for marking items as "loading" and displaying them as such. While item data is loading through the getItem function, it is considered "loading" and is intended to be displayed as such. The same goes for an items children, while their IDs are being fetched through the getChildren function.

The Async Data Loader defines state properties for loading items:

  • state.loadingItems keeps an array of item IDs which are currently loading (via getItem or getChildrenWithData)
  • state.loadingItemChildren keeps an array of item IDs whose children are currently loading (via getChildren or getChildrenWithData)

For a given item, you can check if it is loading its item data or children by calling item.isLoading().

When an item is loading, its item data will be set to the value returned by config.createLoadingItemData. This way, you can customize how loading items are displayed.

Invalidating Item Data

While the Sync Data Loader does not provide a data invalidation concept since it refetches all data during every render anyway, the Async Data Loader caches item data and does not refetch it unless it is invalidated. Methods for invalidating certain items are provided on the item instance:

item.invalidateItemData();
item.invalidateChildrenIds();

The data loader will then refetch the respective data the next render.

Optimistic Item Data Invalidation

Both item.invalidateItemData() and item.invalidateChildrenIds() also accept a boolean parameter optimistic. If set to true, the respective data property will be refetched using the data loader implementation, but the state properties loadingItems and loadingItemChildrens will not be updated, meaning that the tree will not rerender and no items will change into a loading state, and instead continue displaying the old data. Once fetching completes, the new data will be applied into the cache and the tree will rerender with the new data.

item.invalidateItemData(true);
item.invalidateChildrenIds(true);

Optimistic Cache Updates

Instead of invalidating and triggering a refetch, the cached item data can also be updated directly. This will not trigger state updates to the state properties loadingItems and loadingItemChildrens, and the tree will rerender with the new data immediately.

item.updateCachedData({ newData: "abc" });
item.updateCachedChildrenIds(["child-1", "child-2", "child-3"]);

Loading unmounted items

Headless Tree automatically schedules loading of items that are visibly mounted in the tree, i.e. items that are inside folders that are currently expanded. If you need to retrieve information about items that are not currently mounted, you can use the tree.loadItemData(itemId) or tree.loadChildrenIds(itemId) methods to trigger loading of specific items or their children. The methods will directly return a promise that resolves with the retrieved information, and the information will be inserted into the HT cache from where it can be used via methods like item.getItemData(), item.getChildren() or item.getItemName().

Note that item.getItemMeta() will only return information about items that are visibly mounted in the tree, even if the data was forcefully loaded via tree.loadItemData(itemId) or tree.loadChildrenIds(itemId), since that method returns information that relies on the position of the item in the tree, which is not known for unmounted items.