ES6与React,不能setState - '无法读取属性'setState'的undefined'?

时间:2016-01-10 21:23:05

标签: javascript reactjs ecmascript-6

我正在研究为ES5做的反应拖放教程,但我正在使用ES6。

这是我的代码:

  class Song extends React.Component{
    constructor(props){
      super(props);
      this.state = {isPlaying:false,dragMode:editMode == 1 ? true : false,eligibleToDrag: false};
      //this.dragStart = this.dragStart.bind(this);
    }

    _clickAction(){

      if(this.state.dragMode == false){
        changeSong(this.props.arrIndex); 
        playSong();
      }
    }

    dragStart(e){
      Array.prototype.forEach.call(document.getElementById('screen2').getElementsByClassName('track'),function(e1){
        if(e1.getAttribute('data-songid') == e.currentTarget.getAttribute('data-songid')){
          this.setState({eligibleToDrag: false});
          console.log('ELIGIBLE?',this.state.eligibleToDrag);

        }
        else{
          this.setState({eligibleToDrag: true});
          console.log('ELIGIBLE?',this.state.eligibleToDrag);
        }
      });
      console.log(this.state.eligibleToDrag);
      if(e.currentTarget.parentElement.parentElement.id=="screen"){
        e.dataTransfer.effectAllowed = 'link';
      }
      if(e.currentTarget.parentElement.parentElement.id=="screen2"){
        e.dataTransfer.effectAllowed = 'move';
      }
      draggedObject = e.currentTarget;
      e.dataTransfer.setData("text/html",e.currentTarget);
    }

    dragEnd(e){

    }

    dragOver(e){
      e.preventDefault();
      var relY = e.clientY - e.target.offsetTop;
      var height = e.target.offsetHeight / 2;
      if(e.target.parentNode.parentNode.id == 'screen2'){
        if(relY > height){
          e.target.parentNode.insertBefore(draggedObject,e.target.nextElementSibling);
        }
        else if(relY < height){
          e.target.parentNode.insertBefore(draggedObject,e.target);
        }
      }
      if(e.target.parentNode.parentNode.id == 'screen' && draggedObject.parentNode && draggedObject.parentNode.parentNode.id == 'screen2'){
        draggedObject.remove();
        //e.stopPropagation();
      }
      return;

    }
    render(){
      var isDragMode = this.state.dragMode == true;
      if(isDragMode){
        document.getElementById('screen').classList.add("drag");
        document.getElementById('screen2').classList.add("drag");
      }

      return <div data-songid={this.props.trackInfo.Id} onClick={this._clickAction.bind(this)} className={"track"} draggable={isDragMode ?  "true": "false"} onDragEnd={isDragMode ? this.dragEnd.bind(this) : ''} onDragOver={isDragMode ? this.dragOver.bind(this) : ''} onDragStart={isDragMode ? this.dragStart.bind(this) : ''}>
        <span draggable={"false"}>{this.props.trackInfo.Title}</span><br draggable={"false"} />
        <span draggable={"false"}><i draggable={"false"}>{this.props.trackInfo.Artist}</i></span>
      </div>
    }

  }

该类一直有效,直到我在dragStart(e)上添加Array.prototype.forEach.call部分。然后它开始在控制台中给我“无法读取未定义的属性'setState',指向该部分。我不知道我错过了什么,因为据我所知,我已经在渲染方法中绑定了所有内容。我的猜测是我没有绑定所有内容,但我不确定还能做什么。

1 个答案:

答案 0 :(得分:2)

原因是在forEach调用中,变量this不再引用您的预期。如果您坚持以当前方式执行此操作,则应将最初的this变量向上声明并在forEach函数中引用它:

...
dragStart(e){
  var context = this;
  Array.prototype.forEach.call(document.getElementById('screen2').getElementsByClassName('track'),function(e1){
    if(e1.getAttribute('data-songid') == e.currentTarget.getAttribute('data-songid')){
      context.setState({eligibleToDrag: false}); //change this -> context
      console.log('ELIGIBLE?',context.state.eligibleToDrag);

    }
    else{
      context.setState({eligibleToDrag: true});
      console.log('ELIGIBLE?',context.state.eligibleToDrag);
    }
  });
...

但是,由于您使用的是ES6,因此可以使用Shiny,New,Fat Arrow Function

...
dragStart(e) {
  Array.prototype.forEach.call(document.getElementById('screen2').getElementsByClassName('track'), e1 => {
    //use `this` as per usual
  }
}

以上是可能的,因为ES6 Arrow函数捕获封闭上下文的this

修改

从dandavis提到的内容中添加,您实际上可以在this的回调函数中设置forEach的值。它是forEach函数的最后一个参数。因此,如果OP希望以最快的方式解决这个问题,他可以将this附加到forEach来电的最后一个参数中,它就像魔法一样。

Array.prototype.forEach.call(document.getElementById('screen2').getElementsByClassName('track'), function(e1){
  //As per usual
},this);