自分じゃないReact.Componentから上がってくるドラッグイベントを処理する。

自分じゃないReact.Componentから上がってくるドラッグイベントを処理する。

のはつらい。

同一Component内で処理するのは楽

Marker Workbookではそのようにやりました。簡便に書くと以下。

  startDrag(e){
    e.preventDefault();

    let div = e.currentTarget;
    let store = [];
    let startPosition = {x: e.pageX, y: e.pageY};

    store.push({x: e.pageX, y: e.pageY, movedX: 0, movedY: 0});

    let move = (de) => {
      de.preventDefault();

      let now = {x: de.pageX, y: de.pageY, movedX: de.pageX - startPosition.x, movedY: de.pageY - startPosition.y};
      store.push(now);
    };

    let clear = (ue) =>{
      ue.preventDefault();

      this.setState({mousePositions: store});
      div.removeEventListener('mousemove', move);
      window.removeEventListener('mouseup', clear);
    };

    div.addEventListener('mousemove', move);
    window.addEventListener('mouseup', clear);
  }

  render(){
    return <div className="drag-area" onMouseDown={this.startDrag.bind(this)}>drag area</div>
  }

ドラッグ開始の合図であるonMouseDownと同時に各種イベントリスナーを登録しつつドラッグ終了時のイベントリスナー掃除系のリスナーも登録して一発で全部終わるみたいな感じです。

大体このように終わらせるのが楽でよかった。

下位Componentに何も判断させたくない場合

ショートカットによってドラッグに意味があったりなかったり、発行したりしなかったりというのを下位Componentに書きたくない場合は、開始と終了を上位Componentで判断しなくてはならないので、自ずとそのような副作用が必要になります。

  onPress(e) {
    e.preventDefault();

    this.store = [];
    window.addEventListener('mouseup', this.onRelease);

    let now = {x: e.pageX, y: e.pageY};
    this.store.push({x: e.pageX, y: e.pageY, movedX: 0, movedY: 0});
    this.startPosition = now;
  }

  onRelease(e) {
    e.preventDefault();

    this.setState({mousePositions: this.store});

    this.store = null;
    this.startPosition = null;
    window.removeEventListener('mouseup', this.onRelease);
  }

  onDrag(e) {
    if (!this.store) {
      return;
    }
    e.preventDefault();
    let now = {x: e.pageX, y: e.pageY, movedX: e.pageX - this.startPosition.x, movedY: e.pageY - this.startPosition.y};
    this.store.push(now);
  }

  render() {
    return <Pusher {...{onPress, onDrag}}/>
  }

onPressで初期化して、onDragで下位からデータがどんどんくるのでそれを登録、onReleaseで破棄という流れです。

JavaScriptで確実にmouseupをつかんでblur()する。 - ンンンパでも書きましたがmouseupはつかめたりつかめなかったりするので、これはwindowに登録して確実に取れるようにします。

下位Componentはこのように。

export default class Pusher extends React.Component {
  render() {
    return <div className="drag-area" onMouseDown={this.props.onPress} onMouseMove={this.props.onDrag}>pusher drag area</div>
  }
}

しかし

書いてる途中で、どのような場合でもドラッグ中はドラッグ中の情報を、ドラッグ終了時はドラッグ中の情報を全部集積したのを上位に投げて、それを使うかどうかは上位がステートによって判断すればそれでいいだけやんけと思い当たりました。

ドラッグ中に随時その情報が必要(で連続性を判断しなければならないような)場合は、下位Component側からIDを持ったdispatchをさせるのが良いのかな。

とりあえずそうします。

一応今回のおソース。

github.com