How can we render and update 'participants' property in group call?

I’m making group call features using the documents that sendbird provided.

I use sendbird-calls library.

"sendbird-calls": "^1.9.3",

The documents said, if we want to update the room, we need to use SendBirdCall.fetchRoomById() or SendBirdCall.getCachedRoomById().

When I used these features to render participants, I could not access participants property.


SendBirdCall.fetchRoomById(roomId)
      .then((room) => {
        console.log('fetch room successfully', room, room.participants);
        setRoomCtx(room);
        setParticipants(room.participants);
      ...

As you guys know, we can’t controll participants property.

fetch room successfully 
i { ... }, e []

I want to render and update participants property and then, how can I handle participants property?

I’ve used other methods to update room and render participants.

export function init(appId: string, apiHost?: string, websocketHost?: string): void;
export function authenticate(authOption: AuthOption, handler?: AuthHandler): Promise<User>;
export function deauthenticate(): void;
export function connectWebSocket(): Promise<void>;
export function addListener(id: string, listener: SendBirdCallListener): void;
export function removeListener(id: string): void;
export function removeAllListeners(): void;
export function addRecordingListener(id: string, listener: SendBirdCallRecordingListener): void;
export function removeRecordingListener(id: string): void;
export function removeAllRecordingListeners(): void;
export function dial(params: DialParams, callback?: DialHandler): DirectCall;
export function createDirectCallLogListQuery(params?: DirectCallLogListQueryParams): DirectCallLogListQuery;
export function getCurrentAudioInputDevice(): InputDeviceInfo;
export function getAvailableAudioInputDevices(): InputDeviceInfo[];
export function selectAudioInputDevice(mediaDeviceInfo: InputDeviceInfo): void;
export function getCurrentAudioOutputDevice(): MediaDeviceInfo;
export function getAvailableAudioOutputDevices(): MediaDeviceInfo[];
export function selectAudioOutputDevice(mediaDeviceInfo: MediaDeviceInfo): void;
export function getCurrentVideoInputDevice(): InputDeviceInfo;
export function getAvailableVideoInputDevices(): InputDeviceInfo[];
export function selectVideoInputDevice(mediaDeviceInfo: InputDeviceInfo): void;
export function updateMediaDevices(constraints: { audio: boolean; video: boolean }): void;
export function useMedia(constraints: { audio: boolean; video: boolean }): MediaAccess | undefined;
export function updateCustomItems(callId: string, customItems: CustomItems, callback?: CustomItemsHandler): Promise<CustomItemsResult>;
export function deleteCustomItems(callId: string, customItemKeys: string[], callback?: CustomItemsHandler): Promise<CustomItemsResult>;
export function deleteAllCustomItems(callId: string, callback?: CustomItemsHandler): Promise<CustomItemsResult>;
export function setLoggerLevel(level: LoggerLevel): LoggerLevel;
export function getOngoingCalls(): DirectCall[];
export function getOngoingCallCount(): number;
export function setRingingTimeout(timeout: number): void;
export function setCallConnectionTimeout(timeout: number): void;
export function handleWebhookData(data: WebhookData): void;
export function addDirectCallSound(type: SoundType, url: string): Promise<boolean>;
export function removeDirectCallSound(type: SoundType): boolean;
export function getCall(callId: string): DirectCall;
export function createRoom(params: RoomParams): Promise<Room>;
export function getCachedRoomById(roomId: string): Room;
export function fetchRoomById(roomId: string): Promise<Room>;
export function createRoomListQuery(params: RoomListQueryParams): RoomListQuery;
export function registerPushToken(pushToken: string, tokenType: TokenType): Promise<void>;
export function unregisterPushToken(pushToken: string, tokenType: TokenType): Promise<void>;
export function unregisterAllPushTokens(tokenType: TokenType): Promise<void>;
export const sdkVersion: string;
export const appId: string;
export const currentUser: User;

Hi @Junhee_Lee,

I want to make sure I properly understand the issue. Are you saying when you utilize room.participants, it’s not returning any data for you to utilize?

Hello @Tyler ,

Yes, I just want to render the room.‘participants’.

When I enter the room, I can detect who is coming.

And I need to update and rerender the participants property when the type is on ‘remoteParticipantEntered’.

Here is my entering room code
  const enterRoom = useCallback(() => {
    SendBirdCall.fetchRoomById(roomId)
      .then((room) => {
        console.log('fetch room successfully', room, room.participants);
        setRoomCtx(room);

        const enterParams = { audioEnalbed: true };

        room
          .enter(enterParams)
          .then(() => {
            console.log('User has successfully joined');
            setRoomDone(true);
          })
          .catch((error) => {
            console.log('failed to join room', error);
          });

        room.on('remoteParticipantEntered', (participant) => {
          console.log('@ participant entered', participant);
          console.log(
            '@ SendBirdCall.getCachedRoomById(roomId): ',
            SendBirdCall.getCachedRoomById(roomId),
          );
          setRoomCtx({
            ...roomCtx,
            participants: SendBirdCall.getCachedRoomById(roomId).participants,
          });
        });

        room.on('remoteParticipantExited', (participant) => {
          console.log('@ participant exited', participant);
          SendBirdCall.fetchRoomById(roomId).then((room) => {
            setRoomCtx(room);
            console.log('@ room updated by exit: ', room);
          });
        });

        room.on('remoteParticipantStreamStarted', (participant) => {
          console.log('@ participant stream started', participant);
          SendBirdCall.fetchRoomById(roomId).then((room) => {
            setRoomCtx(room);
            console.log('@ room updated by stream: ', room);
          });
        });

        room.on('remoteAudioSettingsChanged', (participant) => {
          console.log('@ participant audio setting changed', participant);
          SendBirdCall.fetchRoomById(roomId).then((room) => {
            setRoomCtx(room);
            console.log('@ room updated by audio setting: ', room);
          });
        });
      })
      .catch((error) => {
        console.log('error fetching room', error);
      });
  }, [SendBirdCall, setRoomDone, roomId, setRoomCtx, roomCtx]);
setRoomCtx({
  ...roomCtx, 
  participants: SendBirdCall.getCachedRoomById(roomId).participants ,
});

If i return the property participants, it doesn’t work. :smiling_face_with_tear:

I found that property ‘room.participants’ is non-enumerable.

And then I can’t (shallow or deep) copy or return that property.

Do you guys provide other methods for updating and rendering the participants?

Hello @Junhee_Lee,

To me, it seems expected that you would not be able to alter the participants property in any way. It seems like you may need to store participants in a localState, and utilize the event handlers to insert or remove the users from local state as they enter or exit.

1 Like

Hello @Tyler,

I understand what you said.

When we use ‘direct call’ feature, we can detect each other ‘real time’ by using ‘addListener’ method or somethings. And we can store each other by using localState.

Such as ‘direct call’ situation, I want to store the ‘real time’ data participants property (not alter).

But If I store room.participants, I can’t access that property. Because room.participants is non-enummerable.

(room.participants property is pale purple. As far as I can tell you, that property is non-enummerable)

If I just ‘store’ the property, how can I store that data?


The situation is like:

  1. I just wait someone come in.
  2. When someone enter the room,
  3. we can detect room.participants by room.on(remoteParticipantEntered)
  4. And then If we want to store ‘real time’ data about the room, we need to recall SendBirdCall.fetchRoomById.
  5. And then I want to store room data for access room.participants and rendering that data.
  6. But I can’t access the room.participants because, room.participants is non-enummerable.
  7. I want to store room.participants and, Is there any way to store and render room.participants ?
const enterRoom = useCallback(() => {
    SendBirdCall.fetchRoomById(roomId)
      .then((room) => {
        console.log('fetch room successfully', room, room.participants);
        setRoomCtx(room);

        const enterParams = { audioEnalbed: true };

        room
          .enter(enterParams)
          .then(() => {
            console.log('User has successfully joined');
            setRoomDone(true);
          })
          .catch((error) => {
            console.log('failed to join room', error);
          });

        room.on('remoteParticipantEntered', (participant) => {
          console.log('@ participant entered', participant);

          SendBirdCall.fetchRoomById(roomId)
            .then((room) => {
              setRoomCtx(room);
            })
            .catch((error) => {
              console.log('error', error);
            });
        });

Hi @Tyler ,

I’m so sorry, I think it was my mistakes.

I think my destructuring assignment have some problems.

  const enterRoom = useCallback(() => {
    SendBirdCall.fetchRoomById(roomId)
      .then((room) => {
        console.log('fetch room successfully', room, room.participants);
        setRoomCtx(room);

        const enterParams = { audioEnalbed: true };

        room
          .enter(enterParams)
          .then(() => {
            console.log('User has successfully joined');
            setRoomDone(true);
          })
          .catch((error) => {
            console.log('failed to join room', error);
          });

        room.on('remoteParticipantEntered', (participant) => {
          console.log('@ participant entered', participant);

          SendBirdCall.fetchRoomById(roomId)
            .then((room) => {
              setRoomCtx({ ...roomCtx, participants: room.participants });
            })
            .catch((error) => {
              console.log('error', error);
            });
        });

        room.on('remoteParticipantExited', (participant) => {
          console.log('@ participant exited', participant);

          SendBirdCall.fetchRoomById(roomId).then((room) => {
            setRoomCtx({ ...roomCtx, participants: room.participants });
            console.log('@ room updated by exit: ', room);
          });
        });

        room.on('remoteParticipantStreamStarted', (participant) => {
          console.log('@ participant stream started', participant);
          SendBirdCall.fetchRoomById(roomId).then((room) => {
            setRoomCtx(room);
            console.log('@ room updated by stream: ', room);
          });
        });

        room.on('remoteAudioSettingsChanged', (participant) => {
          console.log('@ participant audio setting changed', participant);
          SendBirdCall.fetchRoomById(roomId).then((room) => {
            setRoomCtx(room);
            console.log('@ room updated by audio setting: ', room);
          });
        });
      })
      .catch((error) => {
        console.log('error fetching room', error);
      });
  }, [SendBirdCall, setRoomDone, roomId, setRoomCtx, roomCtx]);

Using this destructuring assignment sentences, I could render and update the room.participants.

setRoomCtx({ ...roomCtx, participants: room.participants });
1 Like