diff --git a/.changeset/real-pants-lie.md b/.changeset/real-pants-lie.md new file mode 100644 index 0000000..7c2422d --- /dev/null +++ b/.changeset/real-pants-lie.md @@ -0,0 +1,6 @@ +--- +'@livekit/react-components': minor +'@livekit/react-core': minor +--- + +Breaking: useRoom().room could be undefined on first render! Fix auto-disconnect when LiveKitRoom component unmounts" diff --git a/packages/components/src/LiveKitRoom.tsx b/packages/components/src/LiveKitRoom.tsx index 98cb034..4f61e59 100644 --- a/packages/components/src/LiveKitRoom.tsx +++ b/packages/components/src/LiveKitRoom.tsx @@ -34,21 +34,22 @@ export const LiveKitRoom = ({ const roomState = useRoom(roomOptions); useEffect(() => { - roomState.connect(url, token, connectOptions).then((room) => { - if (!room) { - return; - } - if (onConnected && room.state === ConnectionState.Connected) { - onConnected(room); - } - }); - + if (roomState.room) { + roomState.connect(url, token, connectOptions).then((room) => { + if (!room) { + return; + } + if (onConnected && room.state === ConnectionState.Connected) { + onConnected(room); + } + }); + } return () => { - if (roomState.connectionState !== ConnectionState.Disconnected) { + if (roomState.room?.state !== ConnectionState.Disconnected) { roomState.room?.disconnect(); } }; - }, []); + }, [roomState.room]); const selectedStageRenderer = stageRenderer ?? StageView; diff --git a/packages/core/src/hooks/useRoom.ts b/packages/core/src/hooks/useRoom.ts index e981317..cfdc497 100644 --- a/packages/core/src/hooks/useRoom.ts +++ b/packages/core/src/hooks/useRoom.ts @@ -9,7 +9,7 @@ import { RoomConnectOptions, ConnectionState, } from 'livekit-client'; -import { useCallback, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; export interface RoomState { connect: (url: string, token: string, options?: RoomConnectOptions) => Promise; @@ -24,7 +24,7 @@ export interface RoomState { } export function useRoom(roomOptions?: RoomOptions): RoomState { - const [room] = useState(new Room(roomOptions)); + const [room, setRoom] = useState(); const [isConnecting, setIsConnecting] = useState(false); const [error, setError] = useState(); const [participants, setParticipants] = useState([]); @@ -33,6 +33,10 @@ export function useRoom(roomOptions?: RoomOptions): RoomState { ConnectionState.Disconnected, ); + useEffect(() => { + setRoom(new Room(roomOptions)); + }, []); + const connectFn = useCallback( async (url: string, token: string, options?: RoomConnectOptions) => { setIsConnecting(true); @@ -65,6 +69,11 @@ export function useRoom(roomOptions?: RoomOptions): RoomState { setConnectionState(state); }; + if (!room) { + setError(new Error('room is not ready yet')); + return; + } + room.once(RoomEvent.Disconnected, () => { room .off(RoomEvent.ParticipantConnected, onParticipantsChanged) @@ -89,7 +98,7 @@ export function useRoom(roomOptions?: RoomOptions): RoomState { .on(RoomEvent.AudioPlaybackStatusChanged, onParticipantsChanged) .on(RoomEvent.ConnectionStateChanged, onConnectionStateChanged); - await room?.connect(url, token, options); + await room.connect(url, token, options); setIsConnecting(false); onSubscribedTrackChanged(); setError(undefined); @@ -105,13 +114,13 @@ export function useRoom(roomOptions?: RoomOptions): RoomState { return undefined; } }, - [], + [room], ); return { connect: connectFn, isConnecting, - room: room, + room, error, participants, audioTracks,