Skip to content

Commit ff9f216

Browse files
committed
perf: use cache to improve navigation performance
1 parent 06ee328 commit ff9f216

File tree

1 file changed

+26
-23
lines changed

1 file changed

+26
-23
lines changed

src/OptionList.tsx

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -182,41 +182,24 @@ const OptionList: React.ForwardRefRenderFunction<ReviseRefOptionListProps> = (_,
182182
};
183183

184184
// ========================== Get Next Matching Node ==========================
185+
const availableNodesRef = React.useRef<EventDataNode<any>[]>([]);
186+
185187
const getNextMatchingNode = (
186-
nodes: EventDataNode<any>[],
187188
currentKey: Key | null,
188189
direction: 'next' | 'prev' = 'next',
189190
): EventDataNode<any> | null => {
190-
const availableNodes: EventDataNode<any>[] = [];
191-
192-
const collectNodes = (nodeList: EventDataNode<any>[]) => {
193-
nodeList.forEach(node => {
194-
if (!node.disabled && node.selectable !== false) {
195-
// only collect selected nodes
196-
if (displayValues?.some(v => v.value === node[fieldNames.value])) {
197-
availableNodes.push(node);
198-
}
199-
}
200-
if (node[fieldNames.children]) {
201-
collectNodes(node[fieldNames.children]);
202-
}
203-
});
204-
};
205-
206-
collectNodes(nodes);
191+
const availableNodes = availableNodesRef.current;
207192

208193
if (availableNodes.length === 0) {
209194
return null;
210195
}
211196

212-
// if no current selected node, return first available node
213197
if (!currentKey) {
214198
return availableNodes[0];
215199
}
216200

217201
const currentIndex = availableNodes.findIndex(node => node[fieldNames.value] === currentKey);
218202

219-
// if current node is not in available nodes list, return first node
220203
if (currentIndex === -1) {
221204
return availableNodes[0];
222205
}
@@ -229,6 +212,27 @@ const OptionList: React.ForwardRefRenderFunction<ReviseRefOptionListProps> = (_,
229212
return availableNodes[nextIndex];
230213
};
231214

215+
React.useEffect(() => {
216+
const nodes: EventDataNode<any>[] = [];
217+
const selectedValueSet = new Set(displayValues?.map(v => v.value));
218+
219+
const collectNodes = (nodeList: EventDataNode<any>[]) => {
220+
nodeList.forEach(node => {
221+
if (!node.disabled && node.selectable !== false) {
222+
if (selectedValueSet.has(node[fieldNames.value])) {
223+
nodes.push(node);
224+
}
225+
}
226+
if (node[fieldNames.children]) {
227+
collectNodes(node[fieldNames.children]);
228+
}
229+
});
230+
};
231+
232+
collectNodes(memoTreeData);
233+
availableNodesRef.current = nodes;
234+
}, [displayValues, memoTreeData]);
235+
232236
// ========================== Active ==========================
233237
const [activeKey, setActiveKey] = React.useState<Key>(null);
234238
const activeEntity = keyEntities[activeKey as SafeKey];
@@ -268,10 +272,9 @@ const OptionList: React.ForwardRefRenderFunction<ReviseRefOptionListProps> = (_,
268272
if (isOverMaxCount) {
269273
event.preventDefault();
270274
const direction = which === KeyCode.UP || which === KeyCode.LEFT ? 'prev' : 'next';
271-
const nextNode = getNextMatchingNode(memoTreeData, activeKey, direction);
275+
const nextNode = getNextMatchingNode(activeKey, direction);
272276
if (nextNode) {
273277
setActiveKey(nextNode[fieldNames.value]);
274-
// ensure scroll to visible area
275278
treeRef.current?.scrollTo({ key: nextNode[fieldNames.value] });
276279
}
277280
} else {
@@ -315,7 +318,7 @@ const OptionList: React.ForwardRefRenderFunction<ReviseRefOptionListProps> = (_,
315318
return;
316319
}
317320

318-
const nextNode = getNextMatchingNode(memoTreeData, key);
321+
const nextNode = getNextMatchingNode(key);
319322
if (nextNode) {
320323
setActiveKey(nextNode[fieldNames.value]);
321324
}

0 commit comments

Comments
 (0)