Skip to content

fix: guard scrollTo against a detached list ref - #1067

Open
hippee-lee wants to merge 1 commit into
react-component:masterfrom
hippee-lee:fix/scrollto-null-ref
Open

fix: guard scrollTo against a detached list ref#1067
hippee-lee wants to merge 1 commit into
react-component:masterfrom
hippee-lee:fix/scrollto-null-ref

Conversation

@hippee-lee

@hippee-lee hippee-lee commented Aug 7, 2026

Copy link
Copy Markdown

NodeList's useImperativeHandle has no dependency array, so every re-render detaches the handle during the commit's mutation phase and re-attaches it in the layout phase. Anything that runs between those two — for example an autoFocus input mounting inside a node title, whose bubbled focus triggers the tree's focus activation and scrollTo — dereferences a null listRef and unmounts the whole React root.

This adds optional chaining at the two listRef.current.scrollTo call sites, plus a regression test that reproduces the crash on first mount (fails with TypeError: Cannot read properties of null (reading 'scrollTo') without the fix).

Downstream, this crash reaches every antd Tree / TreeSelect / Cascader consumer.

All checks pass locally: lint (0 errors), tsc, test --runInBand (223/223), build.

Summary by CodeRabbit

  • Bug Fixes

    • 修复列表尚未完成初始化时执行滚动操作导致页面报错的问题。
    • 改善带有自动聚焦控件的树节点加载体验,避免触发未捕获的全局错误。
  • Tests

    • 新增回归测试,验证列表引用尚未就绪时自动聚焦不会产生错误。

Fixes #1068

@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

@hippee-lee is attempting to deploy a commit to the afc163's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

本次修改使 NodeListTree 在列表引用为空时跳过 scrollTo 调用,并新增自动聚焦场景的异步回归测试。

Changes

滚动调用保护与回归测试

Layer / File(s) Summary
滚动调用保护与回归测试
src/NodeList.tsx, src/Tree.tsx, tests/Tree.spec.tsx
NodeListTree 使用可选链调用列表滚动方法。新增测试验证列表引用尚未附加时,自动聚焦节点不会触发窗口错误。

Estimated code review effort: 2 (简单) | ~10 minutes

Possibly related PRs

  • react-component/tree#1018:同样涉及 Tree.tsxNodeList.tsx 的焦点滚动行为,但该 PR 处理的是鼠标焦点导致的意外滚动。
  • react-component/tree#1065:同样修改 tests/Tree.spec.tsx 中的 scrollTo 测试,但处理的是不同的虚拟列表滚动行为。

Suggested reviewers: zombiej, qdyanbing

Poem

我是小兔,轻跳过列表边缘,
引用未到,滚动先安静停歇。
自动聚焦亮起输入框,
窗口不再传来错误声。
测试守在夜色中,
胡萝卜庆祝修复完成。

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确概括了对 detached list ref 调用 scrollTo 进行保护的主要修复。
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/Tree.spec.tsx (1)

1087-1105: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

补充 NodeList 内层引用的回归覆盖。

当前用例主要验证首次挂载时 Tree.listRef 为空的路径。src/Tree.tsx 的 Line 1410 会直接跳过调用,因此该用例不保证会执行 src/NodeList.tsx 的 Line 164。

如果 NodeList 的内部 VirtualList 引用在重渲染期间为空,而外层 NodeListRef 仍存在,NodeList 的保护可能回归且测试仍会通过。请增加直接覆盖 NodeListRef.scrollTo 的测试,或构造该重渲染时序。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Tree.spec.tsx` around lines 1087 - 1105, 增强测试用例以直接覆盖
NodeListRef.scrollTo 在内部 VirtualList 引用暂时为空时的保护逻辑,而不仅依赖 Tree.listRef 为空导致
Tree.tsx 跳过调用。围绕 NodeListRef.scrollTo 构造重渲染时序或直接调用该方法,验证 NodeList.tsx
中的内部引用缺失不会抛出异常。
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/Tree.spec.tsx`:
- Around line 1087-1105: 增强测试用例以直接覆盖 NodeListRef.scrollTo 在内部 VirtualList
引用暂时为空时的保护逻辑,而不仅依赖 Tree.listRef 为空导致 Tree.tsx 跳过调用。围绕 NodeListRef.scrollTo
构造重渲染时序或直接调用该方法,验证 NodeList.tsx 中的内部引用缺失不会抛出异常。

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cba34716-f27d-4448-86c1-6e6bfd14032d

📥 Commits

Reviewing files that changed from the base of the PR and between b35c7b2 and 4682557.

📒 Files selected for processing (3)
  • src/NodeList.tsx
  • src/Tree.tsx
  • tests/Tree.spec.tsx

@nrps9909 nrps9909 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head 4682557bc0ade17847227be55082b1ca77fa09c8.

The two guards match the transient ref-detachment failure mode without changing normal scrolling: the public Tree.scrollTo path safely no-ops before NodeList attaches, while NodeListRef.scrollTo safely no-ops during the narrower window where its inner VirtualList ref is unavailable. Once either ref is attached, the existing call and arguments are unchanged.

I independently reproduced both layers against base b35c7b28e45e37b09bcbeab8b17dfe0ebb0c5680:

  • the submitted autofocus scenario failed on base with TypeError: Cannot read properties of null (reading 'scrollTo') and passes on this head;
  • a temporary isolated probe that kept NodeListRef attached while withholding the inner VirtualList ref failed on base at NodeList.tsx:164 and passes on this head.

Exact-head validation:

  • npm test -- tests/Tree.spec.tsx --runInBand — 68/68 passed, 8/8 snapshots
  • npm test -- --runInBand — 223/223 passed, 42/42 snapshots
  • npm run tsc — passed
  • npm run lint -- --no-fix — 0 errors (11 existing warnings)
  • Prettier check for all changed files — passed
  • npm run build — passed
  • git diff --check b35c7b28e45e37b09bcbeab8b17dfe0ebb0c5680...4682557bc0ade17847227be55082b1ca77fa09c8 — passed

The repository's GitHub Actions runs are marked action_required with no jobs because the external contribution requires maintainer authorization; this is not a test failure. The Vercel status is likewise an external deployment-authorization gate. I found no blocking issue in the code.

Disclosure: Codex assisted with source tracing, test execution, and drafting this review; I independently verified the reproductions and conclusion against the exact PR head.

@aojunhao123

Copy link
Copy Markdown
Contributor

@li-jia-nan @yoyo837 @zombieJ @afc163 大佬们帮忙看看这个

@aojunhao123

Copy link
Copy Markdown
Contributor

@yoyo837 needs to trigger the workflow again

@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.84%. Comparing base (b35c7b2) to head (4682557).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1067   +/-   ##
=======================================
  Coverage   99.84%   99.84%           
=======================================
  Files          15       15           
  Lines        1314     1314           
  Branches      387      396    +9     
=======================================
  Hits         1312     1312           
  Misses          2        2           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
@yoyo837

yoyo837 commented Aug 24, 2026

Copy link
Copy Markdown
Member

这次改了两处 scrollTo,但新增用例在第一处就被拦住了,第二处修改其实没有测试到。建议再补一个能执行到 NodeList.tsx 的用例。

@nrps9909 nrps9909 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewing this after #1063 merged, I no longer think the reported #1068 reproduction justifies merging this PR as-is.

On current master (528f6b0da40ac7bbc4bee7c676bce45c0df8586b), I added the original minimal scenario—a title <input autoFocus />—and tests/Tree.spec.tsx passed (70/70). For a causal check, I temporarily removed only #1063's event.target === event.currentTarget guard from Tree.onFocus; that same test then failed at Tree.tsx:1413 with TypeError: Cannot read properties of null (reading 'scrollTo'). Restoring the guard made it pass again.

That demonstrates that merged PR #1063 already resolves the reported descendant-autofocus path before Tree.scrollTo is reached. The separate NodeList.tsx optional-chain change may still be defensively useful, but this PR does not include the distinct inner-VirtualList ref-detachment regression requested in the maintainer comment, so there is not yet a failing current-master case proving that remaining change.

Please either close this as superseded for #1068, or narrow it to a separately reproducible NodeList gap with a regression test that fails on current master and passes with the guard.

Disclosure: Codex assisted with source/history tracing, the temporary causal mutation, test execution, and drafting; I verified the commands and exact revisions locally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

4 participants