Javascript 错误:此.detachTracks不是一个函数-Twilio Video React

Javascript 错误:此.detachTracks不是一个函数-Twilio Video React,javascript,reactjs,twilio,Javascript,Reactjs,Twilio,接下来,我将修复评论中一些过时的组件,我能够将本地和远程曲目连接到Twilio,但只有在房间中有参与者的情况下分离时才会出现错误。你知道怎么解决这个问题吗 完全错误: TypeError: this.detachTracks is not a function Room.onParticipantUnpublishedTrack C:/Users/.../CallRoom.js:87 84 | } 85 | 86 | onParticipantUnpublishedTrack(tr

接下来,我将修复评论中一些过时的组件,我能够将本地和远程曲目连接到Twilio,但只有在房间中有参与者的情况下分离时才会出现错误。你知道怎么解决这个问题吗

完全错误:

TypeError: this.detachTracks is not a function
Room.onParticipantUnpublishedTrack
C:/Users/.../CallRoom.js:87
  84 | }
  85 | 
  86 | onParticipantUnpublishedTrack(track, trackPublication) {
> 87 |  this.detachTracks([track]);
     | ^  88 | }
  89 | 
  90 | roomJoined(room) {

CallRoom.leaveRoom
C:/Users/.../CallRoom.js:43
  40 | }
  41 | 
  42 | leaveRoom() {
> 43 |  this.state.activeRoom.disconnect();
     | ^  44 |  this.setState({ hasJoinedRoom: false, localMediaAvailable: false });
  45 | }
  46 | 
import React, { Component } from 'react';
import Video from 'twilio-video';
import { withRouter } from "react-router-dom";

import { functions } from "../../Fire.js";

class CallRoom extends Component {
    constructor(props) {
        super(props);
        this.joinRoom = this.joinRoom.bind(this);
        this.leaveRoom = this.leaveRoom.bind(this);
        this.roomJoined = this.roomJoined.bind(this);
        this.detachTracks = this.detachTracks.bind(this);
        this.detachParticipantTracks =this.detachParticipantTracks.bind(this);

        this.state = {
            identity: null,
            roomName: '',
            roomNameErr: false, // Track error for room name TextField
            previewTracks: null,
            localMediaAvailable: false,
            hasJoinedRoom: false,
            activeRoom: '' // Track the current active room
        };
    }


    joinRoom() {
        console.log("Joining room '" + this.props.match.params.questionId + "'...");

        // grabToken call
        functions.httpsCallable('grabToken ')(this.props.user.uid).then((response) => {
            console.log(response.data.token)
            // Join the Room with the token from the server and the
            // LocalParticipant's Tracks.
            Video.connect(response.data.token, {name: this.props.match.params.questionId}).then(this.roomJoined, error => {
                console.error('Could not connect to Twilio: ' + error.code + " - " + error.message);
            });
        });
    }

    leaveRoom() {
        this.state.activeRoom.disconnect();
        this.setState({ hasJoinedRoom: false, localMediaAvailable: false });
    }

    getTracks(participant) {
        return Array.from(participant.tracks.values()).filter(function (publication) {
           return publication.track;
        }).map(function (publication) {
           return publication.track;
        });
     }

    attachTracks(tracks, container) {
        tracks.forEach(track => {
            container.appendChild(track.attach());
        });
    }

    detachTracks(tracks) {
        for (let track of tracks) {
            const htmlElements = track.detach();
            for (let htmlElement of htmlElements) {
               htmlElement.remove();
             }
          }
      }

    // Attaches a track to a specified DOM container
    attachParticipantTracks(participant, container, isLocal) {
        var tracks = this.getTracks(participant);
        this.attachTracks(tracks, container, isLocal);
     }

    detachParticipantTracks(participant) {
        var tracks = this.getTracks(participant);
        this.detachTracks(tracks);
    }

    onParticipantDisconnected(participant, error) {
        // When a (remote) participant disconnects, detach the associated tracks
        this.detachParticipantTracks(participant);
    }

    onParticipantUnpublishedTrack(track, trackPublication) {
        this.detachTracks([track]);
    }

    roomJoined(room) {
        // Called when a participant joins a room
        console.log("Joined as '" + this.props.user.uid + "'");
        this.setState({
            activeRoom: room,
            localMediaAvailable: true,
            hasJoinedRoom: true
        });

        // Attach LocalParticipant's Tracks, if not already attached.
        var previewContainer = this.refs.localMedia;
        if (!previewContainer.querySelector('video')) {
            this.attachParticipantTracks(room.localParticipant, previewContainer);
        }

        // Attach the Tracks of the room's participants.
        room.participants.forEach(participant => {
            console.log("Already in Room: '" + participant.identity + "'");
            var previewContainer = this.refs.remoteMedia;
            this.attachParticipantTracks(participant, previewContainer);
        });

        // Participant joining room
        room.on('participantConnected', function (participant) {
            console.log("Joining: '" + participant.identity + "'");

            participant.tracks.forEach(publication => {
                if (publication.isSubscribed) {
                    const track = publication.track;
                    document.getElementById('remote-media').appendChild(track.attach());
                }
            });

            participant.on('trackSubscribed', function (track) {
                document.getElementById('remote-media').appendChild(track.attach());
            });
        });

        // Attach participant’s tracks to DOM when they add a track
        room.on('trackAdded', (track, participant) => {
            console.log(participant.identity + ' added track: ' + track.kind);
            var previewContainer = this.refs.remoteMedia;
            this.attachTracks([track], previewContainer);
        });

        // Detach participant’s track from DOM when they remove a track.
        room.on('trackRemoved', (track, participant) => {
            console.log(participant.identity + ' removed track: ' + track.kind);
            this.detachTracks([track]);
        });

        room.on('participantDisconnected', this.onParticipantDisconnected);
        room.on('trackUnsubscribed', this.onParticipantUnpublishedTrack);

        // Once the local participant leaves the room, detach the Tracks
        // of all other participants, including that of the LocalParticipant.
        room.on('disconnected', () => {
            if (this.state.previewTracks) {
                this.state.previewTracks.forEach(track => {
                    track.stop();
                });
            }
            this.detachParticipantTracks(room.localParticipant);
            room.participants.forEach(this.detachParticipantTracks);
            this.setState({
                activeRoom: null
            })
            this.setState({ hasJoinedRoom: false, localMediaAvailable: false });
        });
    }

    render() {
        // Only show video track after user has joined a room
        let showLocalTrack = this.state.localMediaAvailable ? (
            <div className="flex-item">
                <div ref="localMedia" />
            </div>
        ) : '';
        // Hide 'Join Room' button if user has already joined a room.
        let joinOrLeaveRoomButton = this.state.hasJoinedRoom ? (
            <button className="s-btn-danger" onClick={this.leaveRoom}>Leave Room</button>
                    ) : (
                        <button className="s-btn" onClick={this.joinRoom}>Join Room</button>
                    );
        return (    
            <div className="flex-container">
                {showLocalTrack}
                <div className="flex-item">
                    {joinOrLeaveRoomButton}
                </div>
                <div className="flex-item" ref="remoteMedia" id="remote-media" />
            </div>
        );
    }
}

export default withRouter(CallRoom);

{
  "name": "app_name",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "firebase": "^7.2.3",
    "formik": "^2.0.3",
    "react": "^16.11.0",
    "react-burger-menu": "^2.6.11",
    "react-confirm": "^0.1.18",
    "react-dnd": "^5.0.0",
    "react-dnd-html5-backend": "^3.0.2",
    "react-dom": "^16.11.0",
    "react-firebase-file-uploader": "^2.4.3",
    "react-flexbox-grid": "^2.1.2",
    "react-ga": "^2.7.0",
    "react-icons": "^3.8.0",
    "react-modal": "^3.11.1",
    "react-responsive": "^8.0.1",
    "react-router-dom": "^5.1.2",
    "react-scripts": "^3.3.0",
    "react-star-ratings": "^2.3.0",
    "react-stripe-elements": "^6.0.1",
    "react-sweet-progress": "^1.1.2",
    "react-toastify": "^5.4.0",
    "react-with-separator": "^1.2.0",
    "twilio-video": "^2.0.1",
    "yup": "^0.27.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}
反应成分:

TypeError: this.detachTracks is not a function
Room.onParticipantUnpublishedTrack
C:/Users/.../CallRoom.js:87
  84 | }
  85 | 
  86 | onParticipantUnpublishedTrack(track, trackPublication) {
> 87 |  this.detachTracks([track]);
     | ^  88 | }
  89 | 
  90 | roomJoined(room) {

CallRoom.leaveRoom
C:/Users/.../CallRoom.js:43
  40 | }
  41 | 
  42 | leaveRoom() {
> 43 |  this.state.activeRoom.disconnect();
     | ^  44 |  this.setState({ hasJoinedRoom: false, localMediaAvailable: false });
  45 | }
  46 | 
import React, { Component } from 'react';
import Video from 'twilio-video';
import { withRouter } from "react-router-dom";

import { functions } from "../../Fire.js";

class CallRoom extends Component {
    constructor(props) {
        super(props);
        this.joinRoom = this.joinRoom.bind(this);
        this.leaveRoom = this.leaveRoom.bind(this);
        this.roomJoined = this.roomJoined.bind(this);
        this.detachTracks = this.detachTracks.bind(this);
        this.detachParticipantTracks =this.detachParticipantTracks.bind(this);

        this.state = {
            identity: null,
            roomName: '',
            roomNameErr: false, // Track error for room name TextField
            previewTracks: null,
            localMediaAvailable: false,
            hasJoinedRoom: false,
            activeRoom: '' // Track the current active room
        };
    }


    joinRoom() {
        console.log("Joining room '" + this.props.match.params.questionId + "'...");

        // grabToken call
        functions.httpsCallable('grabToken ')(this.props.user.uid).then((response) => {
            console.log(response.data.token)
            // Join the Room with the token from the server and the
            // LocalParticipant's Tracks.
            Video.connect(response.data.token, {name: this.props.match.params.questionId}).then(this.roomJoined, error => {
                console.error('Could not connect to Twilio: ' + error.code + " - " + error.message);
            });
        });
    }

    leaveRoom() {
        this.state.activeRoom.disconnect();
        this.setState({ hasJoinedRoom: false, localMediaAvailable: false });
    }

    getTracks(participant) {
        return Array.from(participant.tracks.values()).filter(function (publication) {
           return publication.track;
        }).map(function (publication) {
           return publication.track;
        });
     }

    attachTracks(tracks, container) {
        tracks.forEach(track => {
            container.appendChild(track.attach());
        });
    }

    detachTracks(tracks) {
        for (let track of tracks) {
            const htmlElements = track.detach();
            for (let htmlElement of htmlElements) {
               htmlElement.remove();
             }
          }
      }

    // Attaches a track to a specified DOM container
    attachParticipantTracks(participant, container, isLocal) {
        var tracks = this.getTracks(participant);
        this.attachTracks(tracks, container, isLocal);
     }

    detachParticipantTracks(participant) {
        var tracks = this.getTracks(participant);
        this.detachTracks(tracks);
    }

    onParticipantDisconnected(participant, error) {
        // When a (remote) participant disconnects, detach the associated tracks
        this.detachParticipantTracks(participant);
    }

    onParticipantUnpublishedTrack(track, trackPublication) {
        this.detachTracks([track]);
    }

    roomJoined(room) {
        // Called when a participant joins a room
        console.log("Joined as '" + this.props.user.uid + "'");
        this.setState({
            activeRoom: room,
            localMediaAvailable: true,
            hasJoinedRoom: true
        });

        // Attach LocalParticipant's Tracks, if not already attached.
        var previewContainer = this.refs.localMedia;
        if (!previewContainer.querySelector('video')) {
            this.attachParticipantTracks(room.localParticipant, previewContainer);
        }

        // Attach the Tracks of the room's participants.
        room.participants.forEach(participant => {
            console.log("Already in Room: '" + participant.identity + "'");
            var previewContainer = this.refs.remoteMedia;
            this.attachParticipantTracks(participant, previewContainer);
        });

        // Participant joining room
        room.on('participantConnected', function (participant) {
            console.log("Joining: '" + participant.identity + "'");

            participant.tracks.forEach(publication => {
                if (publication.isSubscribed) {
                    const track = publication.track;
                    document.getElementById('remote-media').appendChild(track.attach());
                }
            });

            participant.on('trackSubscribed', function (track) {
                document.getElementById('remote-media').appendChild(track.attach());
            });
        });

        // Attach participant’s tracks to DOM when they add a track
        room.on('trackAdded', (track, participant) => {
            console.log(participant.identity + ' added track: ' + track.kind);
            var previewContainer = this.refs.remoteMedia;
            this.attachTracks([track], previewContainer);
        });

        // Detach participant’s track from DOM when they remove a track.
        room.on('trackRemoved', (track, participant) => {
            console.log(participant.identity + ' removed track: ' + track.kind);
            this.detachTracks([track]);
        });

        room.on('participantDisconnected', this.onParticipantDisconnected);
        room.on('trackUnsubscribed', this.onParticipantUnpublishedTrack);

        // Once the local participant leaves the room, detach the Tracks
        // of all other participants, including that of the LocalParticipant.
        room.on('disconnected', () => {
            if (this.state.previewTracks) {
                this.state.previewTracks.forEach(track => {
                    track.stop();
                });
            }
            this.detachParticipantTracks(room.localParticipant);
            room.participants.forEach(this.detachParticipantTracks);
            this.setState({
                activeRoom: null
            })
            this.setState({ hasJoinedRoom: false, localMediaAvailable: false });
        });
    }

    render() {
        // Only show video track after user has joined a room
        let showLocalTrack = this.state.localMediaAvailable ? (
            <div className="flex-item">
                <div ref="localMedia" />
            </div>
        ) : '';
        // Hide 'Join Room' button if user has already joined a room.
        let joinOrLeaveRoomButton = this.state.hasJoinedRoom ? (
            <button className="s-btn-danger" onClick={this.leaveRoom}>Leave Room</button>
                    ) : (
                        <button className="s-btn" onClick={this.joinRoom}>Join Room</button>
                    );
        return (    
            <div className="flex-container">
                {showLocalTrack}
                <div className="flex-item">
                    {joinOrLeaveRoomButton}
                </div>
                <div className="flex-item" ref="remoteMedia" id="remote-media" />
            </div>
        );
    }
}

export default withRouter(CallRoom);

{
  "name": "app_name",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "firebase": "^7.2.3",
    "formik": "^2.0.3",
    "react": "^16.11.0",
    "react-burger-menu": "^2.6.11",
    "react-confirm": "^0.1.18",
    "react-dnd": "^5.0.0",
    "react-dnd-html5-backend": "^3.0.2",
    "react-dom": "^16.11.0",
    "react-firebase-file-uploader": "^2.4.3",
    "react-flexbox-grid": "^2.1.2",
    "react-ga": "^2.7.0",
    "react-icons": "^3.8.0",
    "react-modal": "^3.11.1",
    "react-responsive": "^8.0.1",
    "react-router-dom": "^5.1.2",
    "react-scripts": "^3.3.0",
    "react-star-ratings": "^2.3.0",
    "react-stripe-elements": "^6.0.1",
    "react-sweet-progress": "^1.1.2",
    "react-toastify": "^5.4.0",
    "react-with-separator": "^1.2.0",
    "twilio-video": "^2.0.1",
    "yup": "^0.27.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

看起来您只是忘记了在构造函数中绑定participantUnpublishedTrack。看起来您需要遍历并手动绑定需要使用此
的每个函数,或者将它们转换为箭头函数。我在这里看到几个会抛出错误的
leaveRoom
attachParticipantTracks
onParticipantDisconnected
你完全正确,让我试试看,谢谢!这解决了问题,谢谢你的额外一双眼睛!