Skip to content

Conversation

@zenggpzqbx
Copy link

@zenggpzqbx zenggpzqbx commented Dec 19, 2025

PR: ant-design/ant-design#56204
fix:ant-design/ant-design#55955
保持原有的动画写法不变,初次加载的时候动画是正常的,之后的每次加载都不正常。主要原因在下方这张图里
first
我想到了两种解决方案,这两种解决方案都在RcImage里面改动,改动后那个div就会在关闭的时候销毁掉,若是有更好的建议也可以帮我改进一下,谢谢大佬!
第一种方案
second
第二种方案
third

Summary by CodeRabbit

  • Bug 修复

    • 优化预览挂载与显示逻辑,遮罩仅在打开时呈现并支持点击关闭,提升稳定性与关闭交互可靠性。
  • 新功能 / 改进

    • 调整动画与渲染边界,预览主体、翻页控件和底部在独立动画块内协同显示,过渡更顺滑。
    • 底部区域新增进度显示并接受更多交互控制(翻页、缩放、重置等),交互体验更丰富。
  • 测试

    • 更新受控显示相关测试断言以匹配当前可见性行为。

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link

vercel bot commented Dec 19, 2025

@zenggpzqbx is attempting to deploy a commit to the React Component Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link

coderabbitai bot commented Dec 19, 2025

Walkthrough

将 Preview 的动画边界与 DOM 结构重组:根容器变为静态外层,遮罩与主体分别由两个 CSSMotion 控制,主体(Body/Close/PrevNext/Footer)迁移到内部 motion 子树,显示由 portalRender && open 控制,Footer 新增 showProgress(<=50 字)。

Changes

Cohort / File(s) 变更摘要
Preview 渲染与动效边界
src/Preview/index.tsx
将根由单一 CSSMotion 拆成外层静态 root div + 两个 CSSMotion(mask 与 inner content);zIndex 与根样式移至外层;Body、CloseBtn、PrevNext、Footer 等移入内部 motion 子树;动效类名/style 合并到内部容器;Footer 增加 showProgress 且透传相关交互 props;可见性仍由 portalRender && open 控制。
单元测试断言 & 格式调整
tests/controlled.test.tsx, tests/previewGroup.test.tsx
controlled.test.tsx 中对 .rc-image-preview 的断言方向变更(期望由不存在 -> 存在);previewGroup.test.tsx 仅移除 JSX 尾部逗号的格式化调整,运行行为不变。
导出/公共签名
(无文件变更)
公共导出与 API/类型签名未发生变化。

Sequence Diagram(s)

sequenceDiagram
  participant App as 使用方
  participant Portal as Portal
  participant Root as 外层 RootDiv
  participant MotionMask as CSSMotion(mask)
  participant MotionBody as CSSMotion(body)
  participant Body as Body/Close/PrevNext/Footer

  Note over App,Portal: 用户或外部切换 preview open 且 portalRender = true
  App->>Portal: render preview (portalRender)
  Portal->>Root: 挂载外层 root div(应用 zIndex/基础样式)
  alt portalRender && open
    Root->>MotionMask: mount 遮罩(可点击关闭)
    MotionMask-->>Root: 遮罩动效/可交互
    Root->>MotionBody: mount 主体子树(动效边界内)
    MotionBody->>Body: 渲染 Body / Close / PrevNext / Footer
    Body-->>MotionBody: Footer 接收 showProgress 及交互回调
  else not open
    Root-xMotionMask: 不渲染遮罩
    Root-xMotionBody: 不渲染主体
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • zombieJ
  • thinkasany

Poem

🐰✨
我在门廊外轻敲门,遮罩亮起月光温,
子视图在动效里跳,Footer 记下每步痕。
PrevNext 与我并肩走,静候用户轻点唤回春。

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确反映了拉取请求的主要修复内容:解决图像遮罩淡入模糊过渡效果在重新加载时失效的问题。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7150f88 and 7ef9fcc.

📒 Files selected for processing (1)
  • src/Preview/index.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
src/Preview/index.tsx (5)
docs/examples/actionsRender.tsx (2)
  • imageRender (55-57)
  • actionsRender (58-60)
docs/examples/imageRender.tsx (1)
  • imageRender (6-27)
src/Preview/CloseBtn.tsx (1)
  • CloseBtn (9-17)
src/Preview/PrevNext.tsx (1)
  • PrevNext (13-44)
src/Preview/Footer.tsx (1)
  • Footer (46-212)
🔇 Additional comments (4)
src/Preview/index.tsx (4)

392-394: 根容器结构正确

根容器正确地应用了语义类名、移动状态类和样式,同时条件性地应用 zIndex。这个静态容器为内部的两个 CSSMotion 实例提供了稳定的边界。


396-409: 遮罩层动画修复正确实现

将遮罩层包裹在独立的 CSSMotion 中是解决 PR 中描述问题的关键。之前遮罩层通过 {open && ...} 条件渲染,在关闭时会立即销毁 DOM 而无法执行淡出动画。现在通过 visible={portalRender && open} 控制动画生命周期,确保遮罩层在关闭时能够正确执行淡出动画。

实现细节准确:

  • 正确合并 motionClassNamemotionStyle 到遮罩元素
  • 保留了 onClick={onClose} 交互逻辑
  • 使用与内容区域相同的 motionName 确保动画一致性

454-483: Footer 属性传递完整且正确

Footer 组件现在接收了全面的属性集合,包括新增的 showProgressimagetransform 和缩放相关属性(scaleminScalemaxScale),这些属性使 Footer 能够:

  • 根据分组上下文和图片数量控制进度显示
  • 基于当前缩放级别正确启用/禁用缩放操作
  • 将完整的 imagetransform 信息传递给自定义渲染函数

所有传递的属性都与 Footer 组件的接口定义相匹配(基于相关代码片段)。


410-423: 动画结构设计合理且正确

两个 CSSMotion 实例共享相同的配置(motionNamevisible={portalRender && open} 以及动画标志),确保遮罩层和主体内容同步执行进入/退出动画。

包装器 div(420-423 行)的设计恰当地将关切点分离:

  • 外层包装器仅应用动画样式(motionClassNamemotionStyle
  • 内层 body div(426 行)应用业务样式(类名和 bodyStyle,其中包含 transformOrigin 用于缩放效果)

onVisibleChanged 回调仅绑定在主体 CSSMotion 上(第 416 行)是正确的做法,因为只需在可见状态变化时调用一次来处理滚动锁定和状态传播,无需重复触发。


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 and usage tips.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @zenggpzqbx, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

这个拉取请求旨在修复 Ant Design Image 组件中一个动画缺陷,即图片遮罩的淡入模糊过渡动画在初次加载后无法再次正常触发。通过调整 Portal 组件的渲染逻辑,确保相关 DOM 元素在预览关闭时被正确销毁,从而解决了后续动画失效的问题,提升了用户体验。

Highlights

  • 修复图片遮罩动画: 解决了 Image 组件的遮罩淡入模糊过渡动画在首次加载后无法正常工作的问题。
  • Portal 组件的 open 属性调整: 修改了 Portal 组件的 open 属性,使其仅在 portalRenderopen 都为 true 时才保持打开状态,确保在图片预览关闭时 Portal 及其内容被销毁,从而解决动画失效问题。

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

这个 Pull Request 旨在修复 Image 组件在预览模式下,遮罩层的淡入模糊过渡动画在非首次加载时失效的问题。

你提出的修改方案非常简洁且有效。通过将 Portal 组件的 open 属性从 portalRender 修改为 portalRender && open,确保了 Portal 的生命周期与预览的 open 状态正确绑定。这样,当预览关闭时,Portal 及其所有子组件(包括 CSSMotion)都会被正确卸载。当预览再次打开时,它们会被重新挂载,从而能够可靠地触发 CSSMotion 的入场动画。

这个解决方案是声明式的,并且比在回调函数中重置状态的替代方案更清晰,因为它将逻辑控制在组件的渲染方法内部,降低了组件间的耦合。

整体来看,这是一个高质量的修复,代码清晰,解决了根本问题。做得很好!

@codecov
Copy link

codecov bot commented Dec 22, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.41%. Comparing base (d3985c4) to head (d3b489a).

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #492   +/-   ##
=======================================
  Coverage   99.41%   99.41%           
=======================================
  Files          17       17           
  Lines         509      510    +1     
  Branches      152      153    +1     
=======================================
+ Hits          506      507    +1     
  Misses          3        3           

☔ View full report in Codecov by Sentry.
📢 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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/Preview/index.tsx (2)

482-483: 格式问题:分号位置不规范

return 语句的结束部分格式有些奇怪,分号单独占一行。建议调整为更常规的格式。

🔎 建议修复
-  )
-    ;
+  );

412-415: 冗余的包装可以简化

当只有一个类名时,clsx(motionClassName) 是多余的;{{ ...motionStyle }} 的展开也创建了不必要的对象。

🔎 建议简化
              <div
-               className={clsx(motionClassName)}
-               style={{ ...motionStyle }}
+               className={motionClassName}
+               style={motionStyle}
              >
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d3b489a and d98a9ed.

📒 Files selected for processing (1)
  • src/Preview/index.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
src/Preview/index.tsx (5)
docs/examples/actionsRender.tsx (2)
  • imageRender (55-57)
  • actionsRender (58-60)
docs/examples/imageRender.tsx (1)
  • imageRender (6-27)
src/Preview/CloseBtn.tsx (1)
  • CloseBtn (9-17)
src/Preview/PrevNext.tsx (1)
  • PrevNext (13-44)
src/Preview/Footer.tsx (1)
  • Footer (46-212)
🔇 Additional comments (2)
src/Preview/index.tsx (2)

402-443: 结构重构合理,符合修复目标

将主体内容(body、关闭按钮、PrevNext)移入 CSSMotion 内部是正确的做法。这样可以确保:

  1. 内容在关闭时触发离场动画
  2. 动画完成后组件被销毁,从而解决后续加载时模糊过渡失效的问题

visible={portalRender && open} 的条件也能正确控制动画的触发时机。


446-475: Footer 组件属性传递正确

showProgressshowSwitch 属性的传递与 Footer.tsx 中的使用方式一致,所有回调函数和样式属性也都正确传递。

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/Preview/index.tsx (1)

396-401: 遮罩层仍然缺少退出动画

遮罩层通过 {open && ...} 条件渲染,位于 CSSMotion 外部。当 open 变为 false 时,遮罩 DOM 元素会立即被销毁,无法执行淡出动画,而内容区域(Body/CloseBtn/PrevNext/Footer)通过 CSSMotion 会有平滑的退出过渡。这导致视觉不一致:内容淡出但遮罩瞬间消失。

这与之前的评审意见中指出的问题相同。建议将遮罩层也包裹在 CSSMotion 中,或为其创建单独的 CSSMotion 实例,确保遮罩和内容同步进行进入/退出动画。

🔎 建议的修复方案

将遮罩移入 CSSMotion 渲染函数内部,使其与内容同步动画:

-        {/* Mask */}
-        {open && (<div
-            className={clsx(`${prefixCls}-mask`, classNames.mask)}
-            style={styles.mask}
-            onClick={onClose}
-          />
-        )}
         <CSSMotion
           motionName={motionName}
           visible={portalRender && open}
           motionAppear
           motionEnter
           motionLeave
           onVisibleChanged={onVisibleChanged}
         >
           {({ className: motionClassName, style: motionStyle }) => {
             return (
               <div
                 className={clsx(motionClassName)}
                 style={{ ...motionStyle }}
               >
+                {/* Mask */}
+                <div
+                  className={clsx(`${prefixCls}-mask`, classNames.mask)}
+                  style={styles.mask}
+                  onClick={onClose}
+                />
 
                 {/* Body */}
                 <div className={clsx(`${prefixCls}-body`, classNames.body)} style={bodyStyle}>

基于之前的评审意见。

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d98a9ed and 7150f88.

📒 Files selected for processing (3)
  • src/Preview/index.tsx
  • tests/controlled.test.tsx
  • tests/previewGroup.test.tsx
✅ Files skipped from review due to trivial changes (1)
  • tests/previewGroup.test.tsx
🔇 Additional comments (3)
src/Preview/index.tsx (3)

392-394: 根容器结构合理

新的根 div 容器正确处理了基础样式、类名和 zIndex,为遮罩层和内容提供了清晰的承载边界。


402-409: CSSMotion 配置正确

CSSMotion 组件的配置合理:

  • visible={portalRender && open} 正确控制可见性
  • motionAppearmotionEntermotionLeave 均已启用,支持完整的动画生命周期
  • onVisibleChanged 回调正确处理可见性变化

446-475: Footer 组件的 props 接口类型完整且正确

已验证 FooterProps 接口在 src/Preview/Footer.tsx:26 中的定义。该接口继承 Actions 接口,并包含所有传入的 props:

  • showProgress 正确定义为 boolean 类型(第 28 行)
  • 所有操作处理器(onFlipYonFlipXonRotateLeft 等)通过 Actions 接口完整覆盖
  • 其他 props 如 imagetransformscaleminScalemaxScale 等均已正确声明

无类型安全问题。

});

expect(document.querySelector('.rc-image-preview')).toBeFalsy();
expect(document.querySelector('.rc-image-preview')).toBeTruthy();
Copy link

@coderabbitai coderabbitai bot Dec 24, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Check the Preview component's Portal and CSSMotion configuration
rg -n "Portal|CSSMotion" src/Preview/index.tsx -B 3 -A 3

Repository: react-component/image

Length of output: 1595


🏁 Script executed:

# Check the test file context to understand what's being tested
head -50 tests/controlled.test.tsx

Repository: react-component/image

Length of output: 1358


🏁 Script executed:

# Look at the specific test around line 34
sed -n '25,45p' tests/controlled.test.tsx

Repository: react-component/image

Length of output: 611


🏁 Script executed:

# Check if there's cleanup logic for portalRender
rg -n "setPortalRender" src/Preview/index.tsx -B 2 -A 2

Repository: react-component/image

Length of output: 313


🏁 Script executed:

# Check Portal documentation or types to understand its behavior
rg -n "autoDestroy|removeOnLeave|unmountOnExit" src/Preview/index.tsx

Repository: react-component/image

Length of output: 47


🏁 Script executed:

# Look at the complete useLayoutEffect hook for Portal setup
sed -n '375,390p' src/Preview/index.tsx

Repository: react-component/image

Length of output: 530


🏁 Script executed:

# Check if there's a cleanup effect that sets portalRender to false
sed -n '375,410p' src/Preview/index.tsx

Repository: react-component/image

Length of output: 1281


🏁 Script executed:

# Check if there's a cleanup effect that should set portalRender to false
rg -n "useLayoutEffect|useEffect" src/Preview/index.tsx | head -20

Repository: react-component/image

Length of output: 348


🏁 Script executed:

# Look for any other places where portalRender might be reset
rg -n "portalRender" src/Preview/index.tsx

Repository: react-component/image

Length of output: 259


🏁 Script executed:

# Check if there are any comments or logic around the Portal behavior
sed -n '370,420p' src/Preview/index.tsx

Repository: react-component/image

Length of output: 1702


🏁 Script executed:

# Check what onVisibleChanged does in the component
rg -n "onVisibleChanged" src/Preview/index.tsx -B 5 -A 5

Repository: react-component/image

Length of output: 730


🏁 Script executed:

# Look for the onVisibleChanged implementation
sed -n '350,375p' src/Preview/index.tsx

Repository: react-component/image

Length of output: 676


🏁 Script executed:

# Check if there's any logic in onVisibleChanged that should clean up portalRender
rg -n "const onVisibleChanged" src/Preview/index.tsx -A 15

Repository: react-component/image

Length of output: 566


🏁 Script executed:

# Check the full context around the Portal and CSSMotion to understand complete flow
sed -n '375,450p' src/Preview/index.tsx

Repository: react-component/image

Length of output: 2687


🏁 Script executed:

# Check if Portal component unmounts content when open={false} even if portalRender is true
rg -n "Portal" src/Preview/index.tsx -A 1 | head -20

Repository: react-component/image

Length of output: 601


🏁 Script executed:

# Look at the test again to see what was the previous expectation
git log --oneline -20 tests/controlled.test.tsx 2>/dev/null || echo "Git not available, checking for version history"

Repository: react-component/image

Length of output: 136


🏁 Script executed:

# Check if Portal has any mechanism to unmount when its open prop is false
rg -n "portal" src/ -i | grep -i "destroy\|unmount\|remove" | head -10

Repository: react-component/image

Length of output: 47


🏁 Script executed:

# Look at the git diff or commit message to understand the intent
git show --stat 7150f88 2>/dev/null | head -30 || echo "Can't show commit"

Repository: react-component/image

Length of output: 1505


🏁 Script executed:

# Check if there's any issue or comment about this design decision
rg -n "portalRender\|Portal.*open\|root.*always\|element.*mount" src/Preview/index.tsx -B 2 -A 2

Repository: react-component/image

Length of output: 47


🏁 Script executed:

# Look at what the Portal component type expects
rg -n "type PortalProps\|interface PortalProps" -A 10

Repository: react-component/image

Length of output: 47


🏁 Script executed:

# Check if there's a cleanup effect that should reset portalRender
sed -n '376,385p' src/Preview/index.tsx

Repository: react-component/image

Length of output: 344


🏁 Script executed:

# Look at the entire useLayoutEffect hook to see if there's cleanup logic
ast-grep --pattern $'useLayoutEffect(() => {
  $$$
}, [$$$])'

Repository: react-component/image

Length of output: 285


🏁 Script executed:

# Check the actual behavior: does Portal unmount its content when open={false}?
rg -n "open.*false\|Portal.*behavior" . -i | head -10

Repository: react-component/image

Length of output: 47


🏁 Script executed:

# Check if there's a second useEffect that should clean up portalRender
sed -n '375,500p' src/Preview/index.tsx | grep -A 20 "useLayoutEffect\|useEffect"

Repository: react-component/image

Length of output: 784


🏁 Script executed:

# Check the complete useLayoutEffect and surrounding code more carefully
sed -n '362,385p' src/Preview/index.tsx

Repository: react-component/image

Length of output: 655


🏁 Script executed:

# Check if Portal component from @rc-component/portal unmounts when open={false}
web_search @rc-component/portal open property behavior unmount

Repository: react-component/image

Length of output: 115


🏁 Script executed:

# Look at the test again - what was the original expectation before this change?
git log --all --oneline tests/controlled.test.tsx 2>/dev/null | head -5

Repository: react-component/image

Length of output: 310


🏁 Script executed:

# Check if there are other tests that verify the unmount behavior
rg -n "toBeFalsy\|toBeTruthy" tests/controlled.test.tsx

Repository: react-component/image

Length of output: 47


🏁 Script executed:

# Check the git diff for this specific commit to see what changed
git show 7150f88 -- src/Preview/index.tsx | head -100

Repository: react-component/image

Length of output: 3137


🏁 Script executed:

# Check if there's a second effect that should clean up portalRender
sed -n '375,385p' src/Preview/index.tsx | cat -A

Repository: react-component/image

Length of output: 421


🏁 Script executed:

# Look at the entire file around Portal to see if there's cleanup logic we missed
sed -n '375,390p' src/Preview/index.tsx

Repository: react-component/image

Length of output: 530


🏁 Script executed:

# Check if the previous commit (502ca38) has the Portal unmount logic
git show 502ca38:src/Preview/index.tsx 2>/dev/null | grep -A 10 "Portal\|portalRender" | head -30

Repository: react-component/image

Length of output: 1132


🏁 Script executed:

# Check all useEffect/useLayoutEffect hooks in the Preview component
rg -n "useEffect|useLayoutEffect" src/Preview/index.tsx -A 8

Repository: react-component/image

Length of output: 1926


🏁 Script executed:

# Verify the exact behavior - check if Portal.open controls unmounting
rg -n "Portal.*open\|open.*Portal" src/Preview/index.tsx -B 2 -A 2

Repository: react-component/image

Length of output: 47


🏁 Script executed:

# Check if onVisibleChanged should be resetting portalRender
sed -n '368,410p' src/Preview/index.tsx

Repository: react-component/image

Length of output: 1436


🏁 Script executed:

# Check the previous version to see what the old Portal behavior was
git show 502ca38:src/Preview/index.tsx | sed -n '200,250p'

Repository: react-component/image

Length of output: 1179


🏁 Script executed:

# Look at the old test expectation before the change
git show 502ca38:tests/controlled.test.tsx | sed -n '30,40p'

Repository: react-component/image

Length of output: 372


🌐 Web query:

@rc-component/portal open property behavior documentation

💡 Result:

Summary — @rc-component/portal "open" prop

  • Type: boolean.
  • Behavior: when open={true} the Portal mounts/renders its children into the portal container; when open={false} the portal content is not shown.
  • Default: false.
  • Related props: getContainer (customize target container; default document.body) and autoLock (locks page scroll when open).
  • Example: Hello World.

Source: package README/API. [1]


补充缺失的清理逻辑以正确卸载 Portal

useLayoutEffect 中缺少当 open 变为 false 时重置 portalRender 的逻辑。当前代码只在 opentrue 时设置 portalRender = true,但从未重置为 false,导致 Portal 及其内容永久保留在 DOM 中。这会在重复打开/关闭预览的场景中造成内存泄漏。

应在 useLayoutEffect 中添加 else 分支重置状态,或在 onVisibleChanged 回调中当 nextVisible === false 时调用 setPortalRender(false)。测试断言从 toBeFalsy() 改为 toBeTruthy() 反映的是当前的错误行为,而非预期行为。

🤖 Prompt for AI Agents
tests/controlled.test.tsx around line 34: the Portal is never unmounted because
useLayoutEffect only sets portalRender = true when open becomes true and never
resets it to false, causing the Portal to remain in the DOM; fix by adding the
missing cleanup path—either add an else branch in the useLayoutEffect to call
setPortalRender(false) when open is false, or ensure onVisibleChanged calls
setPortalRender(false) when nextVisible === false; after fixing the component
restore the test assertion to expect the portal to be absent (use toBeFalsy())
instead of toBeTruthy().

Copy link
Author

Choose a reason for hiding this comment

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

你的这个问题在逻辑上没有错误,我实际测试过,若添加setPortalRender(false),动画会有问题

Copy link

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

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