函数在需要时触发。我应该如何解决这个问题?

Posted

技术标签:

【中文标题】函数在需要时触发。我应该如何解决这个问题?【英文标题】:Function firing when it wants to. How should I fix this? 【发布时间】:2017-04-27 15:43:58 【问题描述】:

我有这个视频,在 zindex: -1 中播放,上面有一个按钮和一个文本输入。问题是当文本发生变化时,它应该操纵该状态对象,而不是触发可触摸高亮的点击功能。

当我使用昨天给出的建议时,错误变成了警告。如果我在输入框中输入 7 个随机字母,我会收到 7 个警告:“warning bind() you are binding a component method to the component”,这意味着输入框正在继续调用可触摸高亮的函数。

我将这个库用于 React Native 以使用它的流功能:https://github.com/oney/react-native-webrtc。挺好看的!

在其中一个示例中,https://github.com/oney/RCTWebRTCDemo/blob/master/main.js 我正在摆弄这些代码行:

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

当我在文本字段中输入文本时,嵌套在 TouchableHighlight 中的 this._textRoomPress() 函数正在触发。什么!?当我将其注释掉时,它不会触发。

'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;

【问题讨论】:

【参考方案1】:

将其替换为this._textRoomPress.bind(this)

它不是任意触发的,它在每次发出渲染时触发。发生这种情况是因为无论您作为道具传递给对象的任何内容都会在传递之前进行评估(基本上是函数的参数),因此您传递的是函数的返回值,这当然不是您的想。通过传递this._textRoomPress(带有可选的bind,以防你想保留对象的上下文),你传递了一个对函数的引用,该函数稍后将由组件在适当的时间调用(当元素是按下)。

【讨论】:

去掉括号,如:this._textRoomPress.bind(this)。推理是一样的,您正在执行函数并将bind 应用于返回值。您的函数可能没有返回任何内容,因此您得到undefined,因此出现错误。 最后一个问题,为什么会出现这个错误。我确定这是一个绑定问题。 undefined 不是对象评估('pc.textDataChannel.send')。有时它会工作并将数据发送到服务器,但大多数时候它不会。提前感谢@martinarroyo @clxxxii 可能是也可能不是。绑定问题仅影响您通过this 变量访问的值。如果是这种情况,请检查您是否正确绑定了内容或发布代码以便我们查看。另一种可能性是pctextDataChannelundefinedundefined 值没有属性,因此您无法访问它们。检查你更新这两个的地方(因为你说它有时会起作用,很可能你有一些东西改变了它的价值)。 警告:bind():您正在将组件方法绑定到组件。 React 以高性能的方式自动为您完成此操作,因此您可以安全地删除此调用。每次我在输入框中输入内容时都会触发。尽管该功能已附加到可触摸的突出显示,但该功能仍在继续触发。我有两个问题。我会上上面的课。 在更新的代码中,您仍然有 this._textRoomPress() 而不是 this._textRoomPress。因此,该功能继续触发。在这种特殊情况下,您不需要 bind,因为您没有访问 this 参考。【参考方案2】:

由于您使用的是 createClass 而不是 es6 语法,因此所有方法都已自动绑定到组件。只需将您的 onPress 更改为:

onPress=this._textRoomPress>

如果您使用onPress=this._textRoomPress()&gt;,它会在您的组件被渲染时立即调用该函数。

【讨论】:

【参考方案3】:

javascript 中,您使用 &lt;function name&gt;() 调用函数...您在这里所做的只是在每次调用 _renderTextRoom() 时调用该函数,而不是将其分配给 onPress 道具。我建议您将匿名函数作为道具(不调用它)传递,而不是返回对this._textRoomPress 的调用。 ES6 箭头函数让这变得超级简单,因为它们不绑定自己的 this more info here

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

【讨论】:

以上是关于函数在需要时触发。我应该如何解决这个问题?的主要内容,如果未能解决你的问题,请参考以下文章

当触发 iOS Safari 中的虚拟键盘时,它会使我的 CSS 过渡闪烁。如何解决这个问题?

应该如何彻底解决UTF8编码转换成GB2312编码问题?

当我的 useEffect 钩子在 react-apollo 突变后被触发时,如何从反应中解决这个警告?

(Python)我该如何解决这个问题?每次重复我的功能时,我都需要一个计数器

Oracle数据库如何解决ORA-04091触发器/函数不能读它的问题

我应该如何设置这个 c++ 问题?我不需要为我解决它,但是朝着正确的方向前进会很好