Skip to content

refactor: migrate CSSMotionList to FC#82

Open
QDyanbing wants to merge 3 commits into
react-component:masterfrom
QDyanbing:migrate-css-motion-list
Open

refactor: migrate CSSMotionList to FC#82
QDyanbing wants to merge 3 commits into
react-component:masterfrom
QDyanbing:migrate-css-motion-list

Conversation

@QDyanbing

@QDyanbing QDyanbing commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

背景

Ant Design issue ant-design/ant-design#58404 正在清理依赖中的 React class component。@rc-component/motion 中的 CSSMotionList 仍使用 class component,本 PR 将其迁移为函数组件,不引入公开 API 变化。

改动内容

  • CSSMotionList 从 class component 迁移为函数组件。
  • 抽出 keyEntities 派生逻辑,继续复用现有 diffKeys 与移除状态过滤。
  • 使用 hooks 保留 key remove、onVisibleChangedonAllRemoved 的触发行为。
  • 保留 component 默认值、motion props 拆分以及 children ref 兼容分支。

验证

  • npm test
  • npm run lint(仅保留既有 warning)
  • npm run lint:tsc
  • npm run compile(仅保留既有 warning)
  • git diff --check HEAD

Summary by CodeRabbit

  • 重构
    • 将列表动画组件迁移为函数组件,基于合并后的实体集合渲染,并通过状态推导可见性。
    • 精细化移除流程,仅在活动项从“存在”变为“全移除”时触发相关回调;移除后避免重复更新错误状态。
  • 兼容性
    • 公共组件返回类型调整为更通用的组件类型,兼容更多用法场景。
  • 测试
    • 新增覆盖子渲染 ref 参数获取、无需依赖回调的移除稳定性、重复移除不触发异常,以及 keys 字段形状变更后数据属性正确切换的用例。

@vercel

vercel Bot commented Jun 28, 2026

Copy link
Copy Markdown

@QDyanbing 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 Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

CSSMotionList 迁移为 hooks 函数组件,新增 keyEntities 派生与浅比较复用逻辑,调整移除回调触发时机,并补充了 ref、重复移除和对象形状变化相关测试。

Changes

CSSMotionList hooks 迁移

Layer / File(s) Summary
派生实体计算
src/CSSMotionList.tsx
新增 getDerivedKeyEntitiesshallowEqualKeyObjectisSameKeyEntities,用于合并 keys 与既有 keyEntities,并通过浅比较决定是否复用旧数组。
状态同步与移除时机
src/CSSMotionList.tsx
函数组件使用 useState 承载 keyEntities,通过 useIsomorphicLayoutEffect 在 active 数量从大于 0 变为 0 时触发 onAllRemovedremoveKey 改为 useCallback 并标记 STATUS_REMOVED
渲染分支与回归测试
src/CSSMotionList.tsx, tests/CSSMotionList.spec.tsx
渲染逻辑改为在函数组件 return 中遍历 keyEntities 渲染 CSSMotion,并保持 children 的 ref/非 ref 调用分支;新增测试覆盖 component={false}、缺省 onAllRemoved、重复移除与对象形状变化场景。

预估代码审查工作量

🎯 4 (Complex) | ⏱️ ~45 minutes

可能相关的 PR

建议审查者

  • zombieJ

🐇 小兔跳进 hooks 林,
key 影合并又更新。
一闪一闪 ref 轻落,
effect 轻敲回调门,
动画列表稳前行 ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题简洁且准确概括了本次将 CSSMotionList 迁移为函数组件的主要变更。
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.
✨ Finishing Touches
🧪 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.

@codecov

codecov Bot commented Jun 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.68%. Comparing base (de9bede) to head (5f6b97a).

Additional details and impacted files
@@            Coverage Diff             @@
##           master      #82      +/-   ##
==========================================
+ Coverage   98.09%   98.68%   +0.58%     
==========================================
  Files          11       11              
  Lines         421      456      +35     
  Branches      121      137      +16     
==========================================
+ Hits          413      450      +37     
+ Misses          7        6       -1     
+ Partials        1        0       -1     

☔ 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.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request refactors the CSSMotionList component from a class-based component to a functional component using React Hooks. The reviewer provided highly valuable feedback to optimize this migration, which includes: avoiding double renders and layout thrashing by adjusting state during render instead of using a dependency-less useIsomorphicLayoutEffect; removing unsafe Ref mutations from the useState updater function to ensure Concurrent Mode safety; switching to useEffect for non-blocking callbacks; and adding reference equality checks in helper functions to prevent unnecessary comparisons.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/CSSMotionList.tsx Outdated
Comment thread src/CSSMotionList.tsx Outdated
Comment thread src/CSSMotionList.tsx
Comment thread src/CSSMotionList.tsx
Comment thread src/CSSMotionList.tsx

@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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@src/CSSMotionList.tsx`:
- Around line 93-102: shallowEqualKeyObject is too permissive: it only compares
values and can treat objects with different keys as equal, which can retrigger
setKeyEntities in CSSMotionList’s layout effect. Update shallowEqualKeyObject to
verify that target contains each key from origin before comparing values, and
make the comparison stable for NaN values so identical KeyObject metadata does
not cause unnecessary updates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5960b965-f4a0-48a3-8804-38c188e4f65a

📥 Commits

Reviewing files that changed from the base of the PR and between de9bede and 3c9ea1a.

📒 Files selected for processing (1)
  • src/CSSMotionList.tsx

Comment thread src/CSSMotionList.tsx

@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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@tests/CSSMotionList.spec.tsx`:
- Around line 177-191: The CSSMotionList test does not actually verify the
children(props, ref) ref-compatibility path, since rendering still passes even
when ref is undefined. Update the CSSMotionList spec around the
component={false} case to assert the ref is explicitly provided to the child
render function and/or observed on the rendered element, using the CSSMotionList
render prop signature and the existing motion-box assertion to cover the real
branch.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7cc4cd4c-342e-4993-8e5d-1ef73f89ce87

📥 Commits

Reviewing files that changed from the base of the PR and between c6e68ba and 5f6b97a.

📒 Files selected for processing (2)
  • src/CSSMotionList.tsx
  • tests/CSSMotionList.spec.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/CSSMotionList.tsx

Comment on lines +177 to +191
it('should support children ref when component is false', () => {
const CSSMotionList = genCSSMotionList(false);

const { container } = render(
<CSSMotionList motionName="transition" component={false} keys={['a']}>
{({ key }, ref) => (
<div ref={ref as React.Ref<HTMLDivElement>} className="motion-box">
{key}
</div>
)}
</CSSMotionList>,
);

expect(container.children).toHaveLength(1);
expect(container.querySelector('.motion-box')).toHaveTextContent('a');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

这个用例还没有真正覆盖到 children(props, ref) 分支。

现在即使第二个参数是 undefined<div ref={ref as ...}> 也照样能渲染成功,所以这里的断言只能证明文本渲染正常,抓不住 ref 兼容分支的回归。

建议补一个显式断言
 it('should support children ref when component is false', () => {
   const CSSMotionList = genCSSMotionList(false);
+  let receivedRef: React.Ref<HTMLDivElement> | undefined;

   const { container } = render(
     <CSSMotionList motionName="transition" component={false} keys={['a']}>
       {({ key }, ref) => (
-        <div ref={ref as React.Ref<HTMLDivElement>} className="motion-box">
+        (() => {
+          receivedRef = ref as React.Ref<HTMLDivElement>;
+          return (
+            <div ref={receivedRef} className="motion-box">
+              {key}
+            </div>
+          );
+        })()
-          {key}
-        </div>
       )}
     </CSSMotionList>,
   );

   expect(container.children).toHaveLength(1);
   expect(container.querySelector('.motion-box')).toHaveTextContent('a');
+  expect(receivedRef).toBeTruthy();
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('should support children ref when component is false', () => {
const CSSMotionList = genCSSMotionList(false);
const { container } = render(
<CSSMotionList motionName="transition" component={false} keys={['a']}>
{({ key }, ref) => (
<div ref={ref as React.Ref<HTMLDivElement>} className="motion-box">
{key}
</div>
)}
</CSSMotionList>,
);
expect(container.children).toHaveLength(1);
expect(container.querySelector('.motion-box')).toHaveTextContent('a');
it('should support children ref when component is false', () => {
const CSSMotionList = genCSSMotionList(false);
let receivedRef: React.Ref<HTMLDivElement> | undefined;
const { container } = render(
<CSSMotionList motionName="transition" component={false} keys={['a']}>
{({ key }, ref) => (
(() => {
receivedRef = ref as React.Ref<HTMLDivElement>;
return (
<div ref={receivedRef} className="motion-box">
{key}
</div>
);
})()
)}
</CSSMotionList>,
);
expect(container.children).toHaveLength(1);
expect(container.querySelector('.motion-box')).toHaveTextContent('a');
expect(receivedRef).toBeTruthy();
});
🤖 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/CSSMotionList.spec.tsx` around lines 177 - 191, The CSSMotionList test
does not actually verify the children(props, ref) ref-compatibility path, since
rendering still passes even when ref is undefined. Update the CSSMotionList spec
around the component={false} case to assert the ref is explicitly provided to
the child render function and/or observed on the rendered element, using the
CSSMotionList render prop signature and the existing motion-box assertion to
cover the real branch.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant