Skip to content

2.6.3 regression: UncontrolledTreeEnvironment crashes when the data provider resolves undefined (dropped item?.index guard) #443

Description

@acontass

Describe the bug
Since 2.6.3, UncontrolledTreeEnvironment crashes with an unhandled promise rejection — TypeError: Cannot read properties of undefined (reading 'index') — whenever the data provider resolves undefined for one of the requested ids.

The 2.6.3 refactor of the item-map build (20971e8 + 66d6f1d, closing #441) replaced

items.map(item => ({ [item?.index]: item }))

with

for (const item of items) {
  itemMap[item.index] = item;
}

in both the onDidChangeTreeData listener and the onMissingItems batch in UncontrolledTreeEnvironment.tsx — dropping the ?. guard that 2.6.2 had in the onMissingItems path. The library's own StaticTreeDataProvider.getTreeItem returns this.data.items[itemId], i.e. plain undefined for an id not in its record, so the built-in provider is enough to trigger it. Because the throw happens inside an async callback (the setTimeout in onMissingItems), it surfaces as an unhandled rejection and the whole writeItems batch is skipped, so the items that did resolve never render either.

To Reproduce

  1. Create a StaticTreeDataProvider over a record where some item referenced in a parent's children has no entry of its own (the standard shape for lazily-loaded trees: children ids arrive before their bodies, then the provider is swapped once the data lands).
  2. Render it in an UncontrolledTreeEnvironment and expand the parent.
  3. On 2.6.2 the missing id is silently skipped; on 2.6.3 the environment throws Cannot read properties of undefined (reading 'index') and none of the batch's items are written.

Expected behavior
Same as 2.6.2: ids the provider cannot resolve are skipped, and the items that did resolve are still written. E.g.:

for (const item of items) {
  if (item != null) itemMap[item.index] = item;
}

in both loops (the onDidChangeTreeData one never had the guard, but before the refactor an undefined item only produced a garbage { undefined: undefined } entry instead of killing the batch).

Screenshots
N/A — stack trace above.

Additional context

  • react-complex-tree 2.6.3 (regression from 2.6.2), React 19, Chrome (also reproduced headless in Cypress CI).
  • Downstream workaround we ship: subclass StaticTreeDataProvider with a getTreeItems that filters undefined out, which CompleteTreeDataProvider picks up ahead of the per-id fallback.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions