Skip to content

Commit 6693986

Browse files
committed
fix: postpone loadItemData update in getItemData (#158)
1 parent 7a7424f commit 6693986

File tree

3 files changed

+110
-2
lines changed

3 files changed

+110
-2
lines changed

.changeset/chatty-ducks-live.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@headless-tree/core": patch
3+
---
4+
5+
fixed an issue where async data loaders cause calling `item.getItemData()` outside of the component calling `useTree()` to cause a React warning log (#158)

packages/core/src/features/async-data-loader/feature.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ export const asyncDataLoaderFeature: FeatureImplementation = {
118118
}
119119

120120
if (!tree.getState().loadingItemData.includes(itemId) && !skipFetch) {
121-
loadItemData(tree, itemId);
121+
setTimeout(() => loadItemData(tree, itemId));
122122
}
123123

124124
return config.createLoadingItemData?.() ?? null;
@@ -134,7 +134,7 @@ export const asyncDataLoaderFeature: FeatureImplementation = {
134134
return [];
135135
}
136136

137-
loadChildrenIds(tree, itemId);
137+
setTimeout(() => loadChildrenIds(tree, itemId));
138138

139139
return [];
140140
},
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import type { Meta } from "@storybook/react";
2+
import React, { type FC, useState } from "react";
3+
import {
4+
type ItemInstance,
5+
asyncDataLoaderFeature,
6+
dragAndDropFeature,
7+
hotkeysCoreFeature,
8+
selectionFeature,
9+
} from "@headless-tree/core";
10+
import { useTree } from "@headless-tree/react";
11+
import cn from "classnames";
12+
import { type DemoItem, createDemoData } from "../utils/data";
13+
14+
const meta = {
15+
title: "React/Guides/Nested Rendering Async",
16+
tags: ["guide/nested-rendering", "homepage"],
17+
} satisfies Meta;
18+
19+
export default meta;
20+
21+
const { asyncDataLoader } = createDemoData();
22+
23+
// story-start
24+
const Item: FC<{ item: ItemInstance<DemoItem> }> = ({ item }) => {
25+
return (
26+
<li
27+
style={{ paddingLeft: `20px` }}
28+
role="treeitem"
29+
aria-selected={item.isSelected()}
30+
aria-expanded={item.isExpanded()}
31+
>
32+
<button {...item.getProps()}>
33+
<div
34+
className={cn("treeitem", {
35+
focused: item.isFocused(),
36+
expanded: item.isExpanded(),
37+
selected: item.isSelected(),
38+
folder: item.isFolder(),
39+
drop: item.isDragTarget(),
40+
})}
41+
>
42+
{item.getItemName()}
43+
</div>
44+
</button>
45+
{item.isExpanded() && item.getChildren().length > 0 && (
46+
<ul role="group">
47+
{item.getChildren().map((child) => (
48+
<Item key={child.getKey()} item={child} />
49+
))}
50+
</ul>
51+
)}
52+
</li>
53+
);
54+
};
55+
56+
export const NestedRenderingAsync = () => {
57+
const [state, setState] = useState({});
58+
const tree = useTree<DemoItem>({
59+
state,
60+
setState,
61+
rootItemId: "root",
62+
getItemName: (item) => item.getItemData().name,
63+
isItemFolder: (item) => !!item.getItemData().children,
64+
createLoadingItemData: () => ({
65+
name: "Loading...",
66+
}),
67+
hotkeys: {
68+
customEvent: {
69+
hotkey: "Escape",
70+
handler: () => alert("Hello!"),
71+
},
72+
},
73+
dataLoader: asyncDataLoader,
74+
indent: 20,
75+
features: [
76+
asyncDataLoaderFeature,
77+
selectionFeature,
78+
hotkeysCoreFeature,
79+
dragAndDropFeature,
80+
],
81+
});
82+
83+
return (
84+
<div {...tree.getContainerProps()} className="tree">
85+
<ul role="group" {...tree.getContainerProps()} className="tree">
86+
{tree
87+
.getRootItem()
88+
?.getChildren()
89+
.map((item) => <Item key={item.getKey()} item={item} />)}
90+
</ul>
91+
<div style={tree.getDragLineStyle()} className="dragline" />
92+
<style>
93+
{`
94+
.tree ul {
95+
list-style: none;
96+
padding: 0;
97+
margin: 0;
98+
}
99+
`}
100+
</style>
101+
</div>
102+
);
103+
};

0 commit comments

Comments
 (0)