Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function firing when it wants to. How should I fix this?

I have this video, playing in zindex: -1 with a button and a text input floating over it. The issue is when the text changes, it's supposed to manipulate that state object, not fire the touchable highlight's on click function.

When I use the suggestion given yesterday, the error turns into a warning. If I type 7 random letters in the input box, I'll get 7 warnings saying: "warning bind() you are binding a component method to the component", which means the input box is continuing to call the touchable highlight's function.

I'm using this library for React Native to use it's streaming capabilities: https://github.com/oney/react-native-webrtc. It's pretty nice!

On one of it's examples, https://github.com/oney/RCTWebRTCDemo/blob/master/main.js there are these lines of code I'm fiddling with:

  _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>
      );
    },

When I enter text into the text field, the this._textRoomPress() function nested within the TouchableHighlight is firing. What!? When I comment it out, it doesn't fire.

'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;
like image 245
Dr. Div Avatar asked Dec 12 '16 17:12

Dr. Div


1 Answers

Replace it with this._textRoomPress.bind(this)

It is not firing arbitrarily, it is firing every time a render is issued. That happens because whatever you pass to an object as a prop is evaluated before being passed (as the parameters of a function are, basically), and so what you are passing is the returning value of the function, which of course is not what you want. By passing this._textRoomPress (with the optional bind in case that you want to keep the context of the object), you pass a reference to the function that will be later called by the component on the appropriate time (when the element is pressed).

like image 155
martinarroyo Avatar answered Oct 19 '22 10:10

martinarroyo