jolly-eve-3916
07/31/2025, 8:44 AMtall-airline-16756
07/31/2025, 8:44 AMtall-airline-16756
07/31/2025, 8:45 AMjolly-eve-3916
07/31/2025, 8:47 AMProcessing
transcript - User:
${participant.identity ===
patientId});
// Save to backend...
});
Current cleanup attempts:
- We tried storing the room
reference and calling
room.unregisterTextStreamHand
ler("lk.transcription") in
cleanup
- But this resulted in NO
transcripts being received at
all
- Without cleanup, we get
duplicates
Key observation:
The duplicate happens even
when navigating to a
completely new thread/room,
suggesting handlers might
persist across room instances
somehow.
Is there a recommended
pattern for ensuring only one
handler per room, especially
with React's lifecycle
complexities?tall-airline-16756
07/31/2025, 8:49 AMuseEffect with Proper Cleanup
It’s a good idea to register and unregister the handler inside a useEffect hook. This way, the handler gets cleaned up nicely when your component unmounts or when dependencies change:
useEffect(() => {
if (!room) return;
const handler = async (reader, participant) => {
const text = await reader.readAll();
console.log(`Processing transcript - User: ${participant.identity}`);
// Process the text...
};
// Register the handler
room.registerTextStreamHandler("lk.transcription", handler);
// Cleanup function to unregister the handler
return () => {
room.unregisterTextStreamHandler("lk.transcription");
};
}, [room]);
2. Handle Room Disconnection
To keep things tidy and avoid memory leaks, make sure to clean up handlers when the room disconnects:
useEffect(() => {
if (!room) return;
const cleanup = () => {
room.unregisterTextStreamHandler("lk.transcription");
};
// Register handler
room.registerTextStreamHandler("lk.transcription", handler);
// Cleanup on room disconnection
room.on(RoomEvent.Disconnected, cleanup);
return () => {
cleanup();
room.off(RoomEvent.Disconnected, cleanup);
};
}, [room]);
3. Use Built-in Error Protection
Good news! The LiveKit SDK helps prevent multiple handlers for the same topic. If you try to register a handler when one’s already there, it’ll throw a TypeError. Just make sure your code handles this smoothly.
4. Consider Using the useTextStream Hook
If you want to keep things simple, the LiveKit components library offers a handy useTextStream hook that takes care of managing the lifecycle of text stream handlers for you. Here’s a quick look:
import { useTextStream } from '@livekit/components-react';
function MyComponent() {
const { textStreams } = useTextStream("lk.transcription");
return (
<div>
{textStreams.map((textStream) => (
<div key={textStream.streamInfo.id}>
{textStream.text}
</div>
))}
</div>
);
}
5. Debugging and Observations
Since you noticed duplicates sticking around across room instances, here are a couple of things to double-check:
• Make sure the room object updates correctly and gets cleaned up when you switch between threads.
• Confirm that handlers aren’t being registered multiple times because of re-renders or outdated references.
By following these friendly pointers, you’ll have just one handler running per room and avoid those pesky duplicate calls. If you run into any hiccups, I’m here to help!
Sources: packages/core/src/components/chat.ts | src/room/Room.ts | avatars/hedra/education_avatar/education-frontend/hooks/useTextStreamLogger...