fix: prevent memory leak in VideoMessageRecorder and AudioMessageRecorder#40040
fix: prevent memory leak in VideoMessageRecorder and AudioMessageRecorder#40040Tomeshwari-02 wants to merge 3 commits intoRocketChat:developfrom
Conversation
… audio/video recorder
|
Looks like this PR is not ready to merge, because of the following issues:
Please fix the issues and try again If you have any trouble, please check the PR guidelines |
🦋 Changeset detectedLatest commit: d4bea70 The changes in this PR will be included in the next version bump. This PR includes changesets to release 41 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
WalkthroughReplaced state-managed interval handles with ref-based Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested labels
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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. Comment |
… audio/video recorder
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/meteor/client/views/composer/AudioMessageRecorder/AudioMessageRecorder.tsx (1)
58-76:⚠️ Potential issue | 🟠 MajorGuard
handleRecord()whenaudioRecorder.start()outlives the component.
audioRecorder.start()is awaited at Line 66. If the component unmounts while permission/stream setup is still pending, cleanup runs first, but this continuation can still reach Lines 67-76 afterward, restartchat?.action.performContinuously(...), create a fresh interval, and call state setters on an unmounted component. That recreates the orphaned-timer path this PR is trying to remove. Add a teardown flag and bail out immediately after the await, stopping the recorder again if unmount already happened.Possible guard
+ const didUnmountRef = useRef(false); + const handleRecord = useEffectEvent(async () => { chat?.composer?.setRecordingMode(true); @@ try { await audioRecorder.start(); + if (didUnmountRef.current) { + audioRecorder.stop(() => undefined); + return; + } chat?.action.performContinuously('recording'); @@ useEffect(() => { + didUnmountRef.current = false; handleRecord(); return () => { + didUnmountRef.current = true; handleUnmount(); }; }, [handleUnmount, handleRecord]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/meteor/client/views/composer/AudioMessageRecorder/AudioMessageRecorder.tsx` around lines 58 - 76, The async handleRecord function must bail out if the component unmounted while awaiting audioRecorder.start(); add a local teardown flag (e.g., let cancelled = false) that the cleanup/unmount path sets to true, then immediately after await audioRecorder.start() check the flag and if true call audioRecorder.stop()/cleanup and return; this prevents calling chat?.action.performContinuously, creating recordingIntervalRef, or calling setTime/setRecordingRoomId on an unmounted component—update the cleanup logic to set the flag and clear any interval so the guard works with recordingIntervalRef, chat?.composer?.setRecordingMode, and setRecordingRoomId.
🧹 Nitpick comments (1)
apps/meteor/client/views/composer/AudioMessageRecorder/AudioMessageRecorder.tsx (1)
108-114: Remove the second unmount-only cleanup.The main effect cleanup at Lines 103-105 already calls
handleUnmount(), andhandleUnmount()already clears the interval. This extra effect only duplicates teardown and adds an inline comment plus aneslint-disableto keep it compiling.Suggested simplification
- // Ensure interval is cleared on unmount as a safety net - useEffect(() => { - return () => { - clearRecordingInterval(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []);As per coding guidelines, "Avoid code comments in the implementation".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/meteor/client/views/composer/AudioMessageRecorder/AudioMessageRecorder.tsx` around lines 108 - 114, Remove the redundant unmount-only effect in AudioMessageRecorder.tsx: delete the useEffect block that only calls clearRecordingInterval on unmount (the second effect around useEffect(() => { return () => { clearRecordingInterval(); }; }, [])), since handleUnmount already performs the same cleanup; ensure handleUnmount remains responsible for clearing the interval and remove the related eslint-disable comment.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@apps/meteor/client/views/composer/VideoMessageRecorder/VideoMessageRecorder.tsx`:
- Around line 115-118: The unmount cleanup currently only calls
clearRecordingInterval() and VideoRecorder.stop(), leaving the
UserAction.performContinuously interval running; update the component cleanup
(the returned function in VideoMessageRecorder’s effect/unmount path) to also
call UserAction.stop() when a recording is active (use the same recording
state/flag used by handleRecord() or check VideoRecorder.isRecording if
available) so the interval started by UserAction.performContinuously(...) is
cleared; ensure you reference and call UserAction.stop() alongside
clearRecordingInterval() and VideoRecorder.stop() to fully tear down the
recording-related timers.
---
Outside diff comments:
In
`@apps/meteor/client/views/composer/AudioMessageRecorder/AudioMessageRecorder.tsx`:
- Around line 58-76: The async handleRecord function must bail out if the
component unmounted while awaiting audioRecorder.start(); add a local teardown
flag (e.g., let cancelled = false) that the cleanup/unmount path sets to true,
then immediately after await audioRecorder.start() check the flag and if true
call audioRecorder.stop()/cleanup and return; this prevents calling
chat?.action.performContinuously, creating recordingIntervalRef, or calling
setTime/setRecordingRoomId on an unmounted component—update the cleanup logic to
set the flag and clear any interval so the guard works with
recordingIntervalRef, chat?.composer?.setRecordingMode, and setRecordingRoomId.
---
Nitpick comments:
In
`@apps/meteor/client/views/composer/AudioMessageRecorder/AudioMessageRecorder.tsx`:
- Around line 108-114: Remove the redundant unmount-only effect in
AudioMessageRecorder.tsx: delete the useEffect block that only calls
clearRecordingInterval on unmount (the second effect around useEffect(() => {
return () => { clearRecordingInterval(); }; }, [])), since handleUnmount already
performs the same cleanup; ensure handleUnmount remains responsible for clearing
the interval and remove the related eslint-disable comment.
🪄 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: f480a7eb-e646-4df4-adb0-a52fcd4d8e72
📒 Files selected for processing (2)
apps/meteor/client/views/composer/AudioMessageRecorder/AudioMessageRecorder.tsxapps/meteor/client/views/composer/VideoMessageRecorder/VideoMessageRecorder.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: cubic · AI code reviewer
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)
**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation
Files:
apps/meteor/client/views/composer/AudioMessageRecorder/AudioMessageRecorder.tsxapps/meteor/client/views/composer/VideoMessageRecorder/VideoMessageRecorder.tsx
🧠 Learnings (2)
📚 Learning: 2026-03-27T14:52:56.865Z
Learnt from: dougfabris
Repo: RocketChat/Rocket.Chat PR: 39892
File: apps/meteor/client/views/room/contextualBar/Threads/Thread.tsx:150-155
Timestamp: 2026-03-27T14:52:56.865Z
Learning: In Rocket.Chat, there are two different `ModalBackdrop` components with different prop APIs. During review, confirm the import source: (1) `rocket.chat/fuselage` `ModalBackdrop` uses `ModalBackdropProps` based on `BoxProps` (so it supports `onClick` and other Box/DOM props) and does not have an `onDismiss` prop; (2) `rocket.chat/ui-client` `ModalBackdrop` uses a narrower props interface like `{ children?: ReactNode; onDismiss?: () => void }` and handles Escape keypress and outside mouse-up, and it does not forward arbitrary DOM props such as `onClick`. Flag mismatched props (e.g., `onDismiss` passed to the fuselage component or `onClick` passed to the ui-client component) and ensure the usage matches the correct component being imported.
Applied to files:
apps/meteor/client/views/composer/AudioMessageRecorder/AudioMessageRecorder.tsxapps/meteor/client/views/composer/VideoMessageRecorder/VideoMessageRecorder.tsx
📚 Learning: 2026-01-19T18:08:13.423Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38169
File: packages/ui-client/src/hooks/useGoToDirectMessage.ts:20-27
Timestamp: 2026-01-19T18:08:13.423Z
Learning: In React hooks, including custom hooks like useUserSubscriptionByName, must always be called unconditionally and in the same order on every render, per the Rules of Hooks. Passing empty strings or fallback values to hooks is the correct approach when dealing with potentially undefined values, rather than conditionally calling the hook based on whether the value exists.
Applied to files:
apps/meteor/client/views/composer/VideoMessageRecorder/VideoMessageRecorder.tsx
apps/meteor/client/views/composer/VideoMessageRecorder/VideoMessageRecorder.tsx
Show resolved
Hide resolved
|
Tomeshwari-02 seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
What does this PR do?
Fixes a memory leak in both the video and audio message recorder components.
The
setIntervaltimer was stored inuseState, which caused:setStatecalls on unmounted components (React warning)Changes
useState<ReturnType<typeof setInterval>>withuseReffor interval storageclearIntervalcall inuseEffectcleanup (VideoMessageRecorder)handleUnmountsynchronous so cleanup runs reliably on unmount (AudioMessageRecorder)useEffectwith empty deps to guarantee interval is clearedconsole.log(error)→console.error(error)in catch blockResolves
#40039
Summary by CodeRabbit
Bug Fixes
Chores