Skip to content

fix(message): 修复 StrictMode 下消息入场动画残留固定高度导致折叠卡死#144

Open
SsparKluo wants to merge 1 commit into
lehhair:mainfrom
SsparKluo:fix/entry-grow-strictmode-leak
Open

fix(message): 修复 StrictMode 下消息入场动画残留固定高度导致折叠卡死#144
SsparKluo wants to merge 1 commit into
lehhair:mainfrom
SsparKluo:fix/entry-grow-strictmode-leak

Conversation

@SsparKluo

Copy link
Copy Markdown
Contributor

问题

开发模式(React StrictMode)下,如果用户消息处于折叠状态且其对应的流式响应尚未结束,展开折叠气泡后 content 溢出、输入区域消失。查看 HTML 可见 style="height: 260px"(即折叠预览高度)锁死在消息 wrapper 上,重新切回 tab 后因组件重建而正常。

生产构建(stripped StrictMode)无此问题,deploy/cloudflare 正常运行。

根因

useEntryGrowAnimationmotion/mini animate 播放入场动画(0 → scrollHeight)。React StrictMode 在开发模式会双重调用 useLayoutEffect

  1. 第一次 mount:设置 height=\'0px\',启动 WAAPI 动画。
  2. 立即 cleanup:置 cancelled=true + 清 height + 调用 finish()(标记 isEntryGrowComplete),但没有真正 cancel() 掉 WAAPI 动画
  3. 第二次 mount:因 isEntryGrowComplete 已为 true,effect 提前 return,不再处理动画。
  4. 后台的第一轮动画持续运行至结束,onfinishheight:260px 写回 DOM。.then 中因 cancelled=true 跳过清空 → 高度固化。
  5. 展开折叠后 wrapper 仍被 260px 撑开,content 溢出、不推挤下方布局。

修复

  • 持有 animate() 返回的 controls,cleanup 中调用 controls.stop() 真正终止后台动画。
  • 提取 clear() 函数统一负责清空 height/clipPath,不依赖 cancelled 标志。
  • controls.then(clear) 同时覆盖 resolve(正常结束)和 reject(被 cancel 时 motion 的 finished promise 会 reject)两种情况。
  • cleanup 中同时调用 stop() + clear() + finish(),确保在任何卸载路径下 DOM 都被还原。

验证

  • 开发模式(StrictMode 开启)下,折叠气泡展开后 style.height 正确还原,布局正常。
  • 生产构建不变。
  • TypeScript 类型检查通过,无新增依赖。'

…ght under StrictMode

Dev mode wraps the app in StrictMode, which double-invokes effects
(mount -> cleanup -> mount). The previous cleanup only set a
`cancelled` flag and cleared the inline height, but never actually
cancelled the WAAPI animation, so the first animation kept running in
the background. On finish it wrote the final height (260px) back into
the DOM, while the .then clear was skipped because `cancelled` was
true -- leaving the user bubble pinned at a fixed height after expand.

Now hold the animation controls and truly stop() it on cleanup, and
clear the inline height regardless of cancellation state. Also clears
on both resolve and reject (motion rejects finished on cancel).
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