RxJSとreactでmacアドレス入力ボックスを開発します.


プロジェクト概要
今回はRxJSとreactを使って、主に実現された機能は条件に合う文字1-9、a-fの入力を制限し、2桁ごとに自動的にセミコロンを追加することができる.アイテムはreactのイベント処理をブロックし、同時にset SelectRangeを使用してカーソルを手動で制御します.プロジェクトのデモ、プロジェクトの住所を確認できます.
RxJS概要
RxJSはReactive ExtensのJavaScript上での実現であり、具体的には一連のツールライブラリであり、イベント処理、関数節流、遅延などの関数、RxJSは'ストリーム'の思想を応用しており、同時にイベントと時間の概念を持っている.RxJSは非同期プロセスを処理するためにも使用され、Promiseよりキャンセルや遅延、再試行などの利点がある.Promise vs Observable RxJSには、Observableとobserverの二つの比較的重要な概念があります.Observableはcreate,of,from,from Eventなどの方法を用いてストリームを生成することができ,Observerは対流を観察することができる.最後の2つはsubscribeによって結合され、例は以下の通りである.

var Observable = Rx.Observable.create(observer => {
    observer.next(2);
    observer.complete();
    return  () => console.log('disposed');
});

var Observer = Rx.Observer.create(
    x => console.log('Next:', x),
    err => console.log('Error:', err),
    () => console.log('Completed')
);

var subscription = Observable.subscribe(Observer);
構築から流れ式応用—RxJS詳細解
RxJSについては、Introduction RxJS-Javascript library for functional reactiprove grammingを読むことができます.
プロジェクト構造

    //     ,       
    componentDidMount () {
    this.t = ReactDOM.findDOMNode(this.refs.t)
    let keydownValue = Rx.Observable.fromEvent(this.t,'keydown').map(e => e.key.toUpperCase())
    this.sa = keydownValue.filter(value => value.length === 1 && value.match(/[0-9A-F]/)).subscribe(value => {this.setColon('before');this.insertValue(value); this.setColon();this.setDomValue()})
    //        
    }
    //     
    componentWillUnmount() 
    this.sa.dispose()
    //        
    }
    
    //        ,    
    
    
    //          
    render() {
      return (
        
e.preventDefault()} ref="t"/>
); }
プロジェクトの詳細
まずRx.Observable.froomEventを使って入力ボックスのキーイベントをモニターし、キーのkey値を取得してkeydownValueとして保存します.
 let keydownValue = Rx.Observable.fromEvent(this.t,'keydown')
 .map(e => e.key.toUpperCase())
次に、まず入力文字の場合を考えて、ここで、スクリーニングボタンが要求に合致する場合を表示し、次にsubscribeでデータを処理する.新しい文字を挿入する前と後は、前にコロンを付けるかどうかを判断し、最後にset DomValueを使ってstateに保存されているvalueを入力ボックスに表示させる必要があります.
    this.sa = keydownValue
        .filter(value => value.length === 1 && value.match(/[0-9A-F]/))
        .subscribe(value => {
          this.setColon('before');
          this.insertValue(value); 
          this.setColon();
          this.setDomValue()
        })
コロンの関数set Colonを挿入する必要があるかどうかを判断するには、前に文字がない場合と周りにコロンがある場合を除外する必要があります.
  setColon = type => this.state.value.length && 
      (type !== 'before' ? !this.isNearColon() : !this.isLastColon()) && 
      !(this.state.value.slice(0, this.state.pos).replace(/:/g, '').length%2) && 
      this.insertValue(':')
新しい文字の関数を挿入します.記録されたカーソル位置のpos値に新しい文字を挿入し、カーソル位置を変更します.文字の末尾に未完成の文字ペア(つまり1 f:の形式)があり、また途中に新しい文字列を挿入し、文字ペアが6つに達した場合、最後の文字ペアを削除します.
  insertValue = value => {
    if (this.state.value.length !== 17) {
      this.setState({
      ...this.state,
      value: this.state.value.slice(0, this.state.pos) + 
        value + this.state.value.slice(this.state.pos, this.state.value.length)
      })
      this.setPos(this.state.pos + 1)
      if (this.state.value.split(':').length === 7) {
        this.setState({
        ...this.state, 
        value: this.state.value.slice(0, this.state.value.lastIndexOf(':'))
        })
      }
  }}
続いて、削除の流れについて説明します.スクリーニングボタンの値は「BACKSPACE」の流れで、deleteValue方法とset DomValueを実行します.
    this.sb = keydownValue.filter(value => value === 'BACKSPACE')
    .subscribe(() => {
      this.deleteValue()
      this.setDomValue()
    })
deleteValueは、valueと位置が0より大きい時に実行されます.削除後の文字は、新しい最後の文字がコロンです.自動的に削除されます.
  deleteValue = () => {
    if (this.state.value.length && this.state.pos) {
      this.setState({
      ...this.state, 
      value: this.state.value.slice(0, this.state.pos - 1) + 
      this.state.value.slice(this.state.pos, this.state.value.length)
      })
      this.setPos(this.state.pos - 1)
      if (this.isLastColon()) {
        this.deleteValue()
      }
    }
  }
次に左右の方向キーを押して移動するストリームを購読しましたが、簡単ですので、詳しくは説明しません.
    this.sc = keydownValue
        .filter(value => value === 'ARROWLEFT')
        .subscribe(() => this.moveLeft())
    this.sd = keydownValue
        .filter(value => value === 'ARROWRIGHT')
        .subscribe(() => this.moveRight())
 
      moveLeft = () => this.state.pos > 0 && 
      this.setState({...this.state, pos: this.state.pos - 1})
      moveRight = () => this.state.pos !== this.state.value.length && 
      this.setState({...this.state, pos: this.state.pos + 1})
最後にカーソルをposにジャンプさせる処理で、テキストの選択にはsets SelectRangeが使われますが、前の2つのパラメータが同じ数値であれば、カーソルを指定位置にジャンプさせる効果があります.
    this.se = keydownValue.subscribe(() => this.goPos())
    goPos = () => this.t.setSelectionRange(this.state.pos, this.state.pos)
170624更新
元々のパターンはreactと関係が少ないので、修正して調整しました.主な変化はSubject、set Station Ayncを有効にしたので、ここで紹介します.
Rx.Subject
SubjectはObserableとObserverに引き継がれていますので、ObserableとObserverの両方の方法があります.Observableからのmulticast方法によりsubjectをマウントし、同じ実行環境を持つ複数の新しいObservableを得ることができ、彼に関する購読は実際にsubjectにマウントされている.最後に手動connectが必要です.RxJSコアコンセプトのSubjectは、30日間RxJS(24)に精通しています.Observable operators-multicast、refCount、publish、share
var source = Rx.Observable.from([1, 2, 3]);
var multicasted = source.multicast(new Rx.Subject())

//   `subject.subscribe({...})`  Subject Observer:
multicasted.subscribe({
  next: (v) => console.log('observerA: ' + v)
});
multicasted.subscribe({
  next: (v) => console.log('observerB: ' + v)
});

//  Subject          :
multicasted.connect();
実はrefCountでconnectを回避して、multicast(new Rx.Subject())の代わりにpublishを使って、最後にpublishとrefCountの代わりにshareを使ってもいいです.
var multicasted = source.share()
set StteAsync
コンポーネントが制御されたコンポーネントに変更された後、set Stateの非同期特性が示され、set Stateの次のステップがset Stateを取得するのは最新のstateではなく、プログラムの正常な使用に影響を与えました.例えば、以前の追加関数の購読.後のinserValueとset Colonは最新のstateで判断する必要があります.
    this.sa = keydownValue
      .filter(value => value.length === 1 && value.match(/[0-9A-F]/))
        .subscribe(value => {
          this.setColon('before');
          this.insertValue(value); 
          this.setColon();
          this.setDomValue()
        })
set Stateの第二のパラメータにこの問題を解決するためにコールバック関数を導入することができますので、関数はこのようになりました.
this.sa = keydownValue
  .filter(value => value.length === 1 && value.match(/[0-9A-F]/))
    .subscribe(value => {
      this.setColon('before', () => {
        this.insertValue(value, () => {
          this.setColon()
        })
      })
    })
    
続いてインターネットでset StateAsyncの関数を見つけました.原理はset Stateをpromiseの形式に変換してから、async awaitの文法を使ってstateを修正することができます.Reactでset State同期更新策
  setStateAsync = state => new Promise(resolve => this.setState(state,resolve))
実際の調整
componentDidMountでは、keydownValueをObservableとObserveの方法を同時に持つSubjectに設定します.彼はObserverのonNext方法を使って新しいデータを追加することができます.一方で、Observableのオペレータを使ってデータを処理することができます.
this.keydownValue = new Rx.Subject()
let multicasted = this.keydownValue.map(e => e.key.toUpperCase()).share()
this.sa = multicasted
  .filter(value => value.length === 1 && value.match(/[0-9A-F]/))
    .subscribe(async value => {
    await this.setColon('before')
    await this.insertValue(value)
    await this.setColon()
    this.goPos()
  })
//      
コンポーネントのrender関数は
  
handleE関数は引き続きデフォルトイベントを禁止し、新たに設定したSubject(keydownValue)のオンネクスト方法を呼び出して、keydownValueに結びつけられた購読によってデータを得ることができます.
  handleE = e => {e.preventDefault();this.keydownValue.onNext(e)}