skip to Main Content

I wanted to add an video call feature with WebRTC into my chat webapp. So after writing the code i tested the code. -> No video and in the console the error "Uncaught (in promise) TypeError: Cannot set properties of null (setting ‘srcObject’)"
screenshot error message

My code:

//CallProvider
import React, { createContext, useState, useRef, useEffect } from 'react';
import Peer from 'simple-peer';
import io from 'socket.io-client';

export const VideoCallContext = createContext();

const socket = io('http://localhost:5001');

export function CallProvider({ conv, id, children }) {
  const videoGrid = document.querySelector('#video-grid');
  const [stream, setStream] = useState(null);
  const myVideoRef = useRef(null);
  const peer = new Peer({
    trickle: false,
    stream,
  });
  const peers = {};

  useEffect(() => {
    navigator.mediaDevices
      .getUserMedia({
        video: true,
        audio: true,
      })
      .then((currentStream) => {
        setStream(currentStream);
        myVideoRef.current.srcObject = currentStream.streams[0];
      });

    socket.on('user-connected', (userId) => {
      connectToNewUser(userId, stream);
    });
  });

  socket.on('user-disconnected', (userId) => {
    if (peers[userId]) peers[userId].close();
  });

  function joinCall() {
    peer.on('signal', (data) => {
      socket.emit('join-call', {
        call: conv,
        userId: id,
      });
    });
  }

  function leaveCall() {
    socket.emit('leave-call', {
      call: conv,
      userId: id,
    });
    navigator.mediaDevices.getUserMedia({
      video: false,
      audio: false,
    });
  }

  function connectToNewUser(userId, stream) {
    const call = peer.call(userId, stream);
    const video = document.createElement('video');

    call.on('stream', (userVideoStream) => {
      addVideoStream(video, userVideoStream);
    });

    call.on('close', () => {
      video.remove();
    });
  }

  function addVideoStream(video, stream) {
    video.srcObject = stream;
    video.addEventListener('loadedmetadata', () => {
      video.play();
      video.playsInline = true;
    });
    videoGrid.append(video);
  }

  const value = {
    stream,
    myVideoRef,
    joinCall,
    leaveCall,
  };

  return (
    <VideoCallContext.Provider value={value}>
      {children}
    </VideoCallContext.Provider>
  );
}

// FormCall
//use WebRTC from simplepeer
import React, { useContext } from 'react';
import { Button } from 'react-bootstrap';
import { VideoCallContext } from '../contexts/CallProvider';

export default function FormCall({ id, conv, closeWindow }) {
  const { stream, myVideoRef, joinCall, leaveCall } =
    useContext(VideoCallContext);
  const conversation = conv;

  function closeCall() {
    leaveCall(id);
    closeWindow();
  }

  return (
    <>
      <style>
        {`
          #video-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(210px, 230px));
            grid-auto-rows: auto;
            grid-gap: 5px;
          }
        `}
        {`
          video {
            width: 100%;
            aspect-ratio: 1/1;
            object-fit: cover;
            object-position: center;
            border-radius: 10px;
            overflow: hidden;
          }
        `}
      </style>
      <div className="pb-2" id="video-grid">
        <video playsInline muted ref={myVideoRef} autoPlay></video>
      </div>
      <div className="d-flex flex-row justify-content-around border-top pt-2">
        <Button
          onClick={closeCall}
          className="rounded-circle position-relative"
          style={{ height: '40px', width: '40px' }}
        >
          <i
            className="bi bi-telephone-x-fill position-absolute"
            style={{ left: '28%', top: '20%' }}
          />
        </Button>
      </div>
    </>
  );
}

My installed packages are:
packages

I have looked for an solution for the error. Nothing that is a solution for my problem.

I also have changed the "currentStream" to "currentStream.streams[0]", nothing changed.

2

Answers


  1. When you set const myVideoRef = useRef(null)), you’re setting myVideoRef.current to null.

    If you need to the set srcObject property in your ref:

    const myVideoRef = useRef({ srcObject: null }).

    Now myVideoRef.current.srcObject exists and can be set.

    Login or Signup to reply.
  2. Try
    const value = {
    stream:stream,
    myVideoRef:myVideoRef,
    joinCall:joinCall,
    leaveCall:leaveCall,
    };

    Also i would avoid returning *.Provider itself.

    Wrap CallProvider and it’s children like this:

    <VideoCallContext.Provider value={Your value}>
    <CallProvider >
    <Child1 /> 
    <Child2 />
    </CallProvider>
    </VideoCallContext.Provider>
    

    If You mreturn instance of Context.Provider as Component, then every state mutation rerenders Your Context . The Context is innescesarry then.
    If Your Context fully wraps dependant DOM snippet, then Your Context values are stable.

    At least one of these two has to be changed to solve Your problem.

    P.S.
    As Wesley LeMahieu found ,hence i would use useRef() instead of useRef(null).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search