You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Agora RTC + React时远程流显示异常问题求助

Fix: Remote Stream Not Showing for Earlier Joined Users in Agora RTC React Integration

Hey there, I see the issue you're facing—when a second user joins the channel, they can't see the first user's stream, even though the first user can see theirs. Let's break down why this happens and fix it step by step.

Root Cause

Your current code only handles the user-published event, which triggers when a user publishes their stream after you've joined the channel. But when the second user joins, the first user already published their stream before the second user connected. That means the second user's client never gets the user-published event for the first user, so it never subscribes to or renders their stream.

Additionally, your code uses ReactDOM.render() to add remote stream containers, which replaces the entire content of the remote-stream div every time—so even if you had multiple users, only the last one would show up.

Solution

We need to make two key changes:

  1. After joining the channel, manually subscribe to all existing remote users who've already published their streams.
  2. Append new remote stream containers instead of replacing the entire remote-stream div.

Here's the revised code with fixes marked:

import React, { useEffect, useState, useRef } from "react";
import ReactDOM from "react-dom";
import "./App.css";
import { options, rtc } from "./constants";
import AgoraRTC from "agora-rtc-sdk-ng";

function App() {
  const [joined, setJoined] = useState(false);
  const channelRef = useRef("");
  const remoteRef = useRef(null); // Updated to use ref directly

  async function handleSubmit(e) {
    try {
      if (channelRef.current.value === "") {
        return console.log("Please Enter Channel Name");
      }
      setJoined(true);
      rtc.client = AgoraRTC.createClient({ mode: "rtc", codec: "h264" });
      const uid = await rtc.client.join(
        options.appId,
        channelRef.current.value,
        options.token,
        null
      );

      // Create and play local tracks
      rtc.localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack();
      rtc.localVideoTrack = await AgoraRTC.createCameraVideoTrack();
      rtc.localVideoTrack.play("local-stream");

      // Handle newly published streams (existing code)
      rtc.client.on("user-published", async (user, mediaType) => {
        await rtc.client.subscribe(user);
        console.log("subscribe success");
        renderRemoteUserStream(user, mediaType);
      });

      rtc.client.on("user-unpublished", (user) => {
        const playerContainer = document.getElementById(user.uid);
        playerContainer?.remove();
      });

      // --- NEW: Subscribe to existing remote users after joining ---
      const existingRemoteUsers = rtc.client.remoteUsers;
      for (const user of existingRemoteUsers) {
        // Check if the user has already published streams
        if (user.hasPublished("video") || user.hasPublished("all")) {
          await rtc.client.subscribe(user);
          renderRemoteUserStream(user, "video");
        }
        if (user.hasPublished("audio") || user.hasPublished("all")) {
          await rtc.client.subscribe(user);
          user.audioTrack.play();
        }
      }

      // Publish local tracks
      await rtc.client.publish([rtc.localAudioTrack, rtc.localVideoTrack]);
      console.log("publish success!");
    } catch (error) {
      console.error(error);
    }
  }

  // --- NEW: Helper function to render remote stream without overwriting ---
  function renderRemoteUserStream(user, mediaType) {
    if (mediaType === "video" || mediaType === "all") {
      const playerContainer = document.createElement("div");
      playerContainer.id = user.uid;
      playerContainer.className = "stream";
      // Append instead of replacing
      remoteRef.current.appendChild(playerContainer);
      user.videoTrack.play(user.uid);
    }
    if (mediaType === "audio" || mediaType === "all") {
      user.audioTrack.play();
    }
  }

  async function handleLeave() {
    try {
      const localContainer = document.getElementById("local-stream");
      rtc.localAudioTrack.close();
      rtc.localVideoTrack.close();
      setJoined(false);
      localContainer.textContent = "";

      rtc.client.remoteUsers.forEach((user) => {
        const playerContainer = document.getElementById(user.uid);
        playerContainer?.remove();
      });

      await rtc.client.leave();
    } catch (err) {
      console.error(err);
    }
  }

  return (
    <>
      <div className="container">
        <input 
          type="text" 
          ref={channelRef} 
          id="channel" 
          placeholder="Enter Channel name" 
        />
        <input 
          type="submit" 
          value="Join" 
          onClick={handleSubmit} 
          disabled={joined} 
        />
        <input 
          type="button" 
          value="Leave" 
          onClick={handleLeave} 
          disabled={!joined} 
        />
      </div>
      {joined ? (
        <>
          <div id="local-stream" className="stream local-stream"></div>
          <div 
            id="remote-stream" 
            ref={remoteRef} 
            className="stream remote-stream"
          ></div>
        </>
      ) : null}
    </>
  );
}

export default App;

Key Changes Explained

  1. Subscribe to Existing Users: After joining the channel, we loop through rtc.client.remoteUsers (users already in the channel) and subscribe to their streams manually. This ensures new users see everyone who was already present.
  2. Append Remote Stream Containers: Instead of using ReactDOM.render() which replaces content, we create a div element and append it to the remote-stream container using the ref. This keeps all remote streams visible.
  3. Helper Function: We extracted the stream rendering logic into renderRemoteUserStream to avoid code duplication between handling existing users and new published events.

Now when a second user joins, they'll automatically subscribe to and see the first user's stream, and vice versa for any subsequent users.

内容的提问来源于stack exchange,提问作者Ankit Salvi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 15:07:59