8

Ich habe dieses Video, in zindex: -1 mit einer Taste und einem Text-Eingang über es schweben. Das Problem ist, wenn sich der Text ändert, soll er dieses Zustandsobjekt manipulieren, nicht die Klickfunktion des berührbaren Highlights auslösen.Funktion feuern, wenn es will. Wie soll ich das beheben?

Wenn ich den Vorschlag gestern verwende, wird der Fehler zu einer Warnung. Wenn ich 7 zufällige Buchstaben in das Eingabefeld eintippe, erhalte ich 7 Warnungen mit den Worten: "warning bind() verbinde eine Komponentenmethode mit der Komponente", was bedeutet, dass das Eingabefeld weiterhin die Funktion der berührbaren Hervorhebung aufruft.

Ich verwende diese Bibliothek für React Native, um ihre Streaming-Funktionen zu verwenden: https://github.com/oney/react-native-webrtc. Es ist ziemlich nett!

Auf einem seiner Beispiele, https://github.com/oney/RCTWebRTCDemo/blob/master/main.js gibt es diese Zeilen Code mit Ich Hantieren:

_renderTextRoom() { 
     return (
     <View style={styles.listViewContainer}> 

      <ListView 
      dataSource={this.ds.cloneWithRows(this.state.textRoomData)} 
      enableEmptySections={true} 
      renderRow={rowData => 
       <Text 
       style={styles.whiteOut} 
       >{`${rowData.user}: ${rowData.message}`}</Text>} 
      /> 

      <TextInput 
      style={[styles.whiteOut, styles.bgWhite]} 
      onChangeText={value => this.setState({ textRoomValue: value })} 
      value={this.state.textRoomValue} 
      /> 

      <View style={styles.buttonContainer}> 
      <TouchableHighlight 
       style={styles.button} 
       onPress={this._textRoomPress()}> 
       <Text style={styles.bgWhite}>Send</Text> 
      </TouchableHighlight> 
      </View> 

     </View> 
    ); 
    }, 

Wenn ich Text in das Textfeld eingeben, die this._textRoomPress() Funktion innerhalb des TouchableHighlight verschachtelt feuert. Was!? Wenn ich es auszeichne, feuert es nicht.

'use strict'; 

import React, { Component } from 'react'; 
import { 
    Dimensions, 
    StyleSheet, 
    Text, 
    TouchableHighlight, 
    View, 
    TextInput, 
    ListView, 
    ScrollView 
} from 'react-native'; 

import { userData } from '../utils/Factory'; 

import io from 'socket.io-client'; 

var socket_one = 'https://xxxxxxxxxxxxxx.herokuapp.com'; 

const socket = io.connect(socket_one, { transports: ['websocket'] }); 

import { 
    RTCPeerConnection, 
    RTCMediaStream, 
    RTCIceCandidate, 
    RTCSessionDescription, 
    RTCView, 
    MediaStreamTrack, 
    getUserMedia, 
} from 'react-native-webrtc'; 

const configuration = { "iceServers": [{ "url": "stun:stun.l.google.com:19302" }] }; 

const pcPeers = {}; 
let localStream; 

var width = Dimensions.get('window').width; //full width 
var height = Dimensions.get('window').height; //full height 

function getLocalStream(isFront, callback) { 

    MediaStreamTrack.getSources(sourceInfos => { 
    console.log(sourceInfos); 
    let videoSourceId; 
    for (const i = 0; i < sourceInfos.length; i++) { 
     const sourceInfo = sourceInfos[i]; 
     if (sourceInfo.kind == "video" && sourceInfo.facing == (isFront ? "front" : "back")) { 
     videoSourceId = sourceInfo.id; 
     } 
    } 

    getUserMedia({ 
     audio: true, 
     video: { 
     mandatory: { 
      minWidth: 700, // Provide your own width, height and frame rate here 
      minHeight: 700, 
      minFrameRate: 30 
     }, 
     facingMode: (isFront ? "user" : "environment"), 
     optional: [{ sourceId: sourceInfos.id }] 
     } 
    }, function(stream) { 
     console.log('dddd', stream); 
     callback(stream); 
    }, logError); 
    }); 

} 

function join(roomID) { 
    socket.emit('join', roomID, function(socketIds) { 
    console.log('join', socketIds); 
    for (const i in socketIds) { 
     const socketId = socketIds[i]; 
     createPC(socketId, true); 
    } 
    }); 
} 


function createPC(socketId, isOffer) { 
    const pc = new RTCPeerConnection(configuration); 
    pcPeers[socketId] = pc; 

    pc.onicecandidate = function(event) { 
    // console.warn('onicecandidate', event.candidate); 
    if (event.candidate) { 
     socket.emit('exchange', { 'to': socketId, 'candidate': event.candidate }); 
    } 
    }; 

    function createOffer() { 
    pc.createOffer(function(desc) { 
     console.log('createOffer', desc); 
     pc.setLocalDescription(desc, function() { 
     console.log('setLocalDescription', pc.localDescription); 
     socket.emit('exchange', { 'to': socketId, 'sdp': pc.localDescription }); 
     }, logError); 
    }, logError); 
    } 

    pc.onnegotiationneeded = function() { 
    console.log('onnegotiationneeded'); 
    if (isOffer) { 
     createOffer(); 
    } 
    } 

    pc.oniceconnectionstatechange = function(event) { 
    console.log('oniceconnectionstatechange', event.target.iceConnectionState); 
    if (event.target.iceConnectionState === 'completed') { 
     setTimeout(() => { 
     getStats(); 
     }, 1000); 
    } 
    if (event.target.iceConnectionState === 'connected') { 
     createDataChannel(); 
    } 
    }; 
    pc.onsignalingstatechange = function(event) { 
    console.log('onsignalingstatechange', event.target.signalingState); 
    }; 

    pc.onaddstream = function(event) { 
    console.log('onaddstream', event.stream); 
    // container.setState({ info: 'One peer join!' }); 
    container.setState({ info: 'Connected!' }); 

    const remoteList = container.state.remoteList; 
    remoteList[socketId] = event.stream.toURL(); 
    container.setState({ remoteList: remoteList }); 
    }; 
    pc.onremovestream = function(event) { 
    console.log('onremovestream', event.stream); 
    }; 

    pc.addStream(localStream); 

    function createDataChannel() { 
    if (pc.textDataChannel) { 
     return; 
    } 
    const dataChannel = pc.createDataChannel("text"); 

    dataChannel.onerror = function(error) { 
     console.log("dataChannel.onerror", error); 
    }; 

    dataChannel.onmessage = function(event) { 
     console.log("dataChannel.onmessage:", event.data); 
     container.receiveTextData({ user: socketId, message: event.data }); 
    }; 

    dataChannel.onopen = function() { 
     console.log('dataChannel.onopen'); 
     container.setState({ textRoomConnected: true }); 
    }; 

    dataChannel.onclose = function() { 
     console.log("dataChannel.onclose"); 
    }; 

    pc.textDataChannel = dataChannel; 
    } 
    return pc; 
} 

function exchange(data) { 
    const fromId = data.from; 
    let pc; 
    if (fromId in pcPeers) { 
    pc = pcPeers[fromId]; 
    } else { 
    pc = createPC(fromId, false); 
    } 

    if (data.sdp) { 
    console.log('exchange sdp', data); 
    pc.setRemoteDescription(new RTCSessionDescription(data.sdp), function() { 
     if (pc.remoteDescription.type == "offer") 
     pc.createAnswer(function(desc) { 
      console.log('createAnswer', desc); 
      pc.setLocalDescription(desc, function() { 
      console.log('setLocalDescription', pc.localDescription); 
      socket.emit('exchange', { 'to': fromId, 'sdp': pc.localDescription }); 
      }, logError); 
     }, logError); 
    }, logError); 
    } else { 
    console.log('exchange candidate', data); 
    pc.addIceCandidate(new RTCIceCandidate(data.candidate)); 
    } 
} 

function leave(socketId) { 
    console.log('leave', socketId); 
    const pc = pcPeers[socketId]; 
    const viewIndex = pc.viewIndex; 
    pc.close(); 
    delete pcPeers[socketId]; 

    const remoteList = container.state.remoteList; 
    delete remoteList[socketId] 
    container.setState({ remoteList: remoteList }); 
    container.setState({ info: 'One peer leave!' }); 
} 

socket.on('exchange', function(data) { 
    exchange(data); 
}); 
socket.on('leave', function(socketId) { 
    leave(socketId); 
}); 

socket.on('connect', function(data) { 
    console.log('connected'); 
}); 

function initStream() { 
    getLocalStream(true, function(stream) { 
    localStream = stream; 
    container.setState({ selfViewSrc: stream.toURL() }); 
    // container.setState({ status: 'ready', info: 'Please enter or create room ID' }); 
    container.setState({ status: 'connect', info: 'Connecting' }); 

    if (userData.inDanger) { 
     join(0); 
    } else { 
     join(userData.userName); 
     // join(userData.nowPlaying); 
    } 

    }); 

} 

function logError(error) { 
    console.log("logError", error); 
} 

function mapHash(hash, func) { 
    const array = []; 
    for (const key in hash) { 
    const obj = hash[key]; 
    array.push(func(obj, key)); 
    } 
    return array; 
} 

function _textRoomPress() { 
    if (!container.textRoomValue) { 
    return 
    } 
    const textRoomData = container.textRoomData.slice(); 
    textRoomData.push({ user: 'Me', message: container.textRoomValue }); 
    for (const key in pcPeers) { 
    const pc = pcPeers[key]; 
    pc.textDataChannel.send(container.textRoomValue); 
    } 
    container.setState({ textRoomData, textRoomValue: '' }); 
} 

function getStats() { 
    const pc = pcPeers[Object.keys(pcPeers)[0]]; 
    if (pc.getRemoteStreams()[0] && pc.getRemoteStreams()[0].getAudioTracks()[0]) { 
    const track = pc.getRemoteStreams()[0].getAudioTracks()[0]; 
    console.log('track', track); 
    pc.getStats(track, function(report) { 
     console.log('getStats report', report); 
    }, logError); 
    } 
} 

let container; 

const Stream = React.createClass({ 
    getInitialState: function() { 
    this.ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => true }); 
    return { 
     info: 'Initializing', 
     status: 'init', 
     roomID: '', 
     // isFront: true, 
     isFront: false, 
     selfViewSrc: null, 
     remoteList: {}, 
     textRoomConnected: false, 
     textRoomData: [], 
     textRoomValue: '', 
    }; 
    }, 
    componentDidMount: function() { 
    container = this; 
    initStream(); 
    }, 
    _press(event) { 
    // this.refs.roomID.blur(); 
    this.setState({ status: 'connect', info: 'Connecting' }); 
    join(userData.userName); 
    // join(this.state.roomID); 
    }, 
    _switchVideoType() { 
    const isFront = !this.state.isFront; 
    this.setState({ isFront }); 
    getLocalStream(isFront, function(stream) { 
     if (localStream) { 
     for (const id in pcPeers) { 
      const pc = pcPeers[id]; 
      pc && pc.removeStream(localStream); 
     } 
     localStream.release(); 
     } 
     localStream = stream; 
     container.setState({ selfViewSrc: stream.toURL() }); 

     for (const id in pcPeers) { 
     const pc = pcPeers[id]; 
     pc && pc.addStream(localStream); 
     } 
    }); 
    }, 
    receiveTextData(data) { 
    const textRoomData = this.state.textRoomData.slice(); 
    textRoomData.push(data); 
    this.setState({ textRoomData, textRoomValue: '' }); 
    }, 
    _textRoomPress() { 
    if (!this.state.textRoomValue) { 
     return 
    } 
    const textRoomData = this.state.textRoomData.slice(); 
    textRoomData.push({ user: 'Me', message: this.state.textRoomValue }); 
    for (const key in pcPeers) { 
     const pc = pcPeers[key]; 
     pc.textDataChannel.send(this.state.textRoomValue); 
    } 
    this.setState({ textRoomData, textRoomValue: '' }); 
    }, 
    _renderTextRoom() { 
    return (
     <View style={styles.listViewContainer}> 

       <ListView 
       dataSource={this.ds.cloneWithRows(this.state.textRoomData)} 
       enableEmptySections={true} 
       renderRow={rowData => 
        <Text 
        style={styles.whiteOut} 
        >{`${rowData.user}: ${rowData.message}`}</Text>} 
       /> 

       <TextInput 
       style={[styles.whiteOut, styles.bgWhite]} 
       onChangeText={value => this.setState({ textRoomValue: value })} 
       value={this.state.textRoomValue} 
       /> 


       <View style={styles.buttonContainer}> 
       <TouchableHighlight 
        style={styles.button} 
        onPress={this._textRoomPress()}> 
        <Text style={styles.bgWhite}>Send</Text> 
       </TouchableHighlight> 
       </View> 

      </View> 
    ); 
    }, 
    render() { 
    return (
     <View style={styles.container}> 
     { 
      mapHash(this.state.remoteList, (remote, index) => { 

      return (
       <ScrollView key={index}> 

       <RTCView key={index} streamURL={this.state.selfViewSrc} style={styles.remoteView}> 

       <View style={styles.buttonContainer}> 
        <TouchableHighlight 
        style={styles.button} 
        onPress={this._switchVideoType}> 
        <Text>Switch camera</Text> 
        </TouchableHighlight> 
       </View> 

       <View style={styles.bottomContainer}> 
        {this.state.textRoomConnected && this._renderTextRoom()} 
       </View> 

       </RTCView> 

      ) 

      }) 
     } 
     </View> 
    ); 
    } 
}); 

const styles = StyleSheet.create({ 
    container: { 
    flex: 10, 
    // justifyContent: 'center', 
    backgroundColor: 'rgba(0,0,0, .0)', 
    }, 
    topContainer: { 
    flex: 10, 
    backgroundColor: '#c7c7c7', 
    }, 
    bottomContainer: { 
    flex: 1, 
    justifyContent: 'flex-end', 
    // backgroundColor: '#ffeeff', 
    'zIndex': 1, 
    backgroundColor: 'rgba(0,0,0, .0)', 

    }, 
    selfView: { 
    width: 0, 
    height: 0 
    }, 
    remoteView: { 
    flex: 1, 
    'zIndex': -1, 
    // backgroundColor: '#c7c7c7', 
    backgroundColor: '#f0f0f0', 
    width: width, 
    height: height - 25, 
    resizeMode: 'stretch', // or 'stretch' 

    }, 
    welcome: { 
    fontSize: 20, 
    textAlign: 'center', 
    margin: 10, 
    }, 
    listViewContainer: { 
    height: 150, 
    }, 
    buttonContainer: { 
    height: 50, 
    // backgroundColor: 'powderblue', 
    justifyContent: 'center', 
    alignItems: 'center', 
    }, 
    button: { 
    marginTop: 50, 
    marginBottom: 50, 
    padding: 10, 
    paddingLeft: 30, 
    paddingRight: 30, 
    borderWidth: 1, 
    borderColor: 'rgba(0, 0, 0, .75)', 
    }, 
    whiteOut: { 
    // color: "#ffffff", 
    color: "#000", 
    }, 
    bgWhite: { 
    // backgroundColor: "#ffffff" 
    }, 
    listView: { 
    // backgroundColor: "#ffffff", 
    flex: 10, 
    // flexDirection: 'row', 
    // justifyContent: 'center', 
    // alignItems: 'center', 
    } 
}); 

export default Stream; 

Antwort

7

es ersetzen mit this._textRoomPress.bind(this)

Es ist nicht willkürlich abfeuern, es jedes Mal, wenn ein feuert machen ausgegeben wird. Das passiert, weil alles, was Sie an ein Objekt als Requisite übergeben, vor dem Übergeben ausgewertet wird (wie die Parameter einer Funktion im Grunde sind), und was Sie also übergeben, ist der Rückgabewert der Funktion, was Sie natürlich nicht tun wollen. Übergeben Sie this._textRoomPress (mit dem optionalen bind für den Fall, dass Sie den Kontext des Objekts beibehalten möchten) übergeben Sie einen Verweis auf die Funktion, die später von der Komponente zur entsprechenden Zeit aufgerufen wird (wenn das Element gedrückt wird).

+1

Entfernen Sie die Klammer nicht ihre eigenen binden, wie: 'this._textRoomPress.bind (this)'. Die Argumentation ist die gleiche, Sie führen die Funktion aus und wenden 'bind' auf den Rückgabewert an. Ihre Funktion gibt wahrscheinlich nichts zurück, Sie erhalten also undefined, daher der Fehler. – martinarroyo

+0

letzte Frage, warum bekomme ich diesen Fehler. Ich bin mir sicher, dass es ein bindendes Thema ist. undefined ist kein evaluierendes Objekt ('pc.textDataChannel.send'). Manchmal funktioniert es und sendet die Daten an den Server, aber die meiste Zeit nicht. Vielen Dank im Voraus @martinarroyo – clxxxii

+0

@clxxxii Es könnte oder es könnte nicht sein. Bindungsprobleme betreffen nur Werte, auf die Sie über die Variable 'this' zugreifen. Wenn das der Fall ist, überprüfen Sie, ob Sie die Objekte korrekt binden, oder geben Sie den Code ein, damit wir ihn uns ansehen können. Die andere Möglichkeit ist, dass entweder 'pc' oder' textDataChannel' nicht definiert sind.'undefined' Werte haben keine Eigenschaften und daher können Sie nicht darauf zugreifen. Überprüfen Sie die Orte, an denen Sie diese beiden aktualisieren (da Sie sagen, dass es manchmal funktioniert, ist es wahrscheinlich, dass Sie etwas haben, das seinen Wert ändert). – martinarroyo

3

Da Sie createClass und nicht die ES6-Syntax verwenden, sind alle Methoden bereits autobindend für die Komponente. Einfach nur Ihre onPress ändern:

onPress={this._textRoomPress}> 

Wenn Sie onPress={this._textRoomPress()}> verwenden Sie sofort wird diese Funktion jederzeit Ihre Komponente gerendert wird aufgerufen wird.

+0

Ihr habt beide Recht! Ich danke dir sehr! – clxxxii

2

In Javascript verwenden Sie <function name>(), um eine Funktion aufzurufen ... Was Sie hier tun, ist einfach aufrufen diese Funktion jedes Mal, dass _renderTextRoom() aufgerufen wird, anstatt es auf die onPress-Prop zuweisen. Was ich vorschlagen würde ist, dass Sie eine anonyme Funktion als Prop (als Aufruf) übergeben, die dann den Aufruf von zurückgibt. ES6 Pfeil Funktionen machen diese super einfach, weil sie thismore info here

<View style={styles.buttonContainer}> 
    <TouchableHighlight 
     style={styles.button} 
     onPress={() => this._textRoomPress()}> 
     <Text style={styles.bgWhite}>Send</Text> 
    </TouchableHighlight> 
    </View> 
+0

Ihr habt beide recht! Ich danke dir sehr! – clxxxii

Verwandte Themen