Skip to content

fix(motion): attach motionRef to correct DOM element for CSSMotion lifecycle#591

Open
JeremiahGeronimo wants to merge 2 commits intoreact-component:masterfrom
JeremiahGeronimo:fix/motion-ref
Open

fix(motion): attach motionRef to correct DOM element for CSSMotion lifecycle#591
JeremiahGeronimo wants to merge 2 commits intoreact-component:masterfrom
JeremiahGeronimo:fix/motion-ref

Conversation

@JeremiahGeronimo
Copy link
Copy Markdown

@JeremiahGeronimo JeremiahGeronimo commented Mar 29, 2026

Summary

After the resize feature was added, motionRef was passed to DrawerPanel while motion classes/styles were applied to the content-wrapper element. This mismatch caused CSSMotion to lose track of the animated DOM node, breaking lifecycle callbacks (onLeaveEnd, onEnterEnd, etc.).

The fix binds motionRef to the content-wrapper — the same element that receives motion classes — by merging it with wrapperRef via a callback ref (fillWrapperRef). The stale ref passthrough to DrawerPanel has been removed.

What changed

  • Added fillWrapperRef callback that merges wrapperRef (used by resize/drag) and motionRef (used by CSSMotion) on the content-wrapper div
  • Removed containerRef={motionRef} from DrawerPanel, which was the source of the ref mismatch

Summary by CodeRabbit

发布说明

  • Chores(代码维护)
    • 优化抽屉组件对容器与动画/内容的绑定,确保尺寸调整与动画渲染一致,提升交互稳定性与视觉一致性。
    • 精简组件间引用传递,降低实现复杂度并提高可维护性。

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 29, 2026

@JeremiahGeronimo 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
Copy Markdown

coderabbitai bot commented Mar 29, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c1259531-d56f-4853-9f7f-19e8e868d727

📥 Commits

Reviewing files that changed from the base of the PR and between 9026e79 and dfa8d29.

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

Walkthrough

src/DrawerPopup.tsx 的 ref 管理进行了调整:使用 fillRef/mergeRefs 将包装器 DOM 节点与内部 motion/content ref 在同一回调中绑定,并移除了传给 DrawerPanelcontainerRef prop。

Changes

Cohort / File(s) Summary
Ref 管理重构
src/DrawerPopup.tsx
引入并使用 fillRef / mergeRefs 回调,将原先的 wrapperRef 与 motion/content ref 合并到包装器 div 的 ref 回调上;删除传递给 DrawerPanelcontainerRef prop,将容器 ref 责任移至父级包装器。

代码审查工作量估计

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • zombieJ

Poem

🐰 嗨咯,线索绕成圈,
我把 refs 串成一根线;
父容器握住内里心,
prop 离场回调现,
轻轻跳跃,变更欢。

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题清晰准确地描述了主要变更:修复motionRef附加到正确的DOM元素以支持CSSMotion生命周期,这正是PR的核心改动。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

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

Copy link
Copy Markdown
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

This pull request updates DrawerPopup.tsx to improve ref management by introducing a callback that merges wrapperRef and motionRef using the fillRef utility. This change replaces the direct passing of motionRef to DrawerPanel. The review feedback suggests renaming the new callback to mergeRefs to better reflect its functionality of combining multiple refs, which enhances code clarity.

Copy link
Copy Markdown

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

🧹 Nitpick comments (1)
src/DrawerPopup.tsx (1)

307-313: 建议让组合后的 ref 保持稳定。

Line 355 每次渲染都会重新执行 fillWrapperRef(motionRef) 并创建新的 callback ref。React 会先用 null 清理旧 ref,再把同一节点设置给新 ref;这里又把这两个值继续转发给 motionRef。拖拽 resize 会频繁重渲染,这会让 CSSMotion 的节点跟踪比必要的更脆弱。建议保留一个稳定的 wrapper ref callback,并通过中间 ref 保存最新的 motionRef;同时把 node 类型改成 HTMLDivElement | null 会更符合 React 的 ref 调用约定。

♻️ 可参考的调整
+  const motionWrapperRef = React.useRef<React.Ref<HTMLDivElement>>();
+
-  const fillWrapperRef = React.useCallback(
-    (motionRef: React.Ref<HTMLDivElement>) => (node: HTMLDivElement) => {
-      wrapperRef.current = node;
-      fillRef(motionRef, node);
-    },
-    [],
-  );
+  const fillWrapperRef = useEvent((node: HTMLDivElement | null) => {
+    wrapperRef.current = node;
+    fillRef(motionWrapperRef.current, node);
+  });
       {({ className: motionClassName, style: motionStyle }, motionRef) => {
+        motionWrapperRef.current = motionRef;
         const content = (
           <DrawerPanel
             id={id}
             prefixCls={prefixCls}
             className={clsx(className, drawerClassNames?.section)}
             style={{ ...style, ...styles?.section }}
             {...pickAttrs(props, { aria: true })}
             {...eventHandlers}
           >
             {children}
           </DrawerPanel>
         );
         return (
           <div
-            ref={fillWrapperRef(motionRef)}
+            ref={fillWrapperRef}

Also applies to: 355-355

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/DrawerPopup.tsx` around lines 307 - 313, The current fillWrapperRef
recreates a callback ref on each call which causes React to clear and re-set
refs; make the callback stable by storing the latest motionRef in a ref and
returning a single stable node callback. Concretely: add a ref like
motionRefLatest = useRef<React.Ref<HTMLDivElement> | null>(null), create a
stable node callback (e.g., stableWrapperNodeRef = useCallback((node:
HTMLDivElement | null) => { wrapperRef.current = node;
fillRef(motionRefLatest.current, node); }, [])), and change fillWrapperRef to
set motionRefLatest.current = motionRef and then return stableWrapperNodeRef
(ensure node param type is HTMLDivElement | null). Update references to
fillWrapperRef, wrapperRef, and fillRef accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/DrawerPopup.tsx`:
- Around line 307-313: The current fillWrapperRef recreates a callback ref on
each call which causes React to clear and re-set refs; make the callback stable
by storing the latest motionRef in a ref and returning a single stable node
callback. Concretely: add a ref like motionRefLatest =
useRef<React.Ref<HTMLDivElement> | null>(null), create a stable node callback
(e.g., stableWrapperNodeRef = useCallback((node: HTMLDivElement | null) => {
wrapperRef.current = node; fillRef(motionRefLatest.current, node); }, [])), and
change fillWrapperRef to set motionRefLatest.current = motionRef and then return
stableWrapperNodeRef (ensure node param type is HTMLDivElement | null). Update
references to fillWrapperRef, wrapperRef, and fillRef accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0e6dbd91-aa6d-4880-a844-95f2dcadbeda

📥 Commits

Reviewing files that changed from the base of the PR and between 6a52ca1 and 9026e79.

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

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