🐶 イントロへのイントロ:RX + JSX実験


私は反応が好きです.そして、私はrxjsが好きです.そこで、新しいフレームワークに混ぜてみました.
import { timer } from 'rxjs';

function App() {
  const ticks$ = timer(0, 1000);

  return <div>
    <h1>{ ticks$ }</h1>
    <p>seconds passed</p>
  </div>
}
run this example

TLドクター


Github repo 🙂

巻頭言


私はHackathonのために約1週間でこのレンダリングエンジンを構築した.それは面白いコンセプトであることがわかりました.

概念


反応は、私たちのJSコード(仮想DOMを経て)でDOM「ファーストクラス市民」を作りました.我々はvdomをどこにでも構築でき、それをパスします.
Responseのコンポーネントは基本的にVDOMへのプロパティーのマッピングです.
// React
(props: Object) => vDOM
角度深く統合された観測可能なストリームは、それらのコンポーネントとサービスにネイティブにしました.オブザーバブルは、簡単に動作し、非同期のイベントと更新を調整し、時間内に広がる.
このフレームワークでは、マッププロパティをVDOMに(同様に)反応させます.ここでのみ更新を完全に制御し、ストリームをレンダリングします.propsの入力ストリームをとり,vdomの出力ストリームにマップします.
// This framework
(props$: Observable<Object>) => Observable<vDOM>
ストリームイン.流れ出す.

からの最愛の犬great article
例を挙げましょう.

基本的な使い方


きっと、「Hello World」から始めなければなりません.
import { of } from 'rxjs';

function App() {
  return of(<h1>Hello world!</h1>)
}
of 単一の値を出力する観測可能なオブジェクトを作成する
我々のコンポーネントが静的になるので<h1> そして、それを更新しないでください-我々は観測可能な部分をスキップして、単に要素を返します.
function App() {
  return <h1>Hello world!</h1>
}
ルックスは反応します、それ?より多くの生命を我々のコンポーネントに加えましょう:

タイマー


import { timer } from 'rxjs';
import { map } from 'rxjs/operators';

function TimerApp() {
  return timer(0, 1000).pipe(
    map(tick =>
      <div>
        <h1>{ tick }</h1>
        <p>seconds passed</p>
      </div>
    )
  )
}
timer(n, m) Aを出す0 アットn そして、結果として生じる整数をm 間隔
再び、コンポーネントはVDOMのストリームを返します.コンポーネントが値を出力するたびにVDOMが更新されます.
この例ではtimer は毎秒新しい値を出力します.その価値map 新しいVDOMに、それぞれを表示するtick<h1> .
我々はこれも簡単に行うことができます!
VDOMの子が観測可能なものであるならば、エンジンはそれを聞いて、その値を適当にし始めます.では、移動しましょうtimer 観測できる権利<h1> :
import { timer } from 'rxjs';

function TimerApp() {
  const ticks$ = timer(0, 1000);

  return <div>
    <h1>{ ticks$ }</h1>
    <p>seconds passed</p>
  </div>
}
run this example (これはヘッダと同じです)
これは、きちんとした構文でより良い最新版を定義するのを許します.
コンポーネント関数は一度だけ呼び出されます.観測可能なときtimer(0, 1000) 値を発します- VDOMは、再計算されるか、木の他の部分を更新することなく、適所に更新されます


コンポーネント内のローカルな状態を必要とするときにSubjects 書き、聞く.
被験者はまた、それらに値をプッシュすることができますオブザーバーブルです.それで、我々は両方を聞くことができて、イベントを放出することができます
以下に例を示します:
import { Subject } from 'rxjs';
import { map, startWith } from 'rxjs/operators';

function GreetingApp() {
  const name$ = new Subject();
  const view$ = name$.pipe(
    map(x => x ? `Hello, ${x}!` : ''),
    startWith('')
  );

  return <div>
    <input
      placeholder="enter your name"
      onInput={e => name$.next(e.target.value)}
    />
    { view$ }
  </div>
}
run the greeting input example
上記の例では、テキストフィールドがinput イベント-私たちはname$ ストリームview$ 表示するストリームはname$ 入力ストリーム.
Aを使用していることに注意してくださいstartWith 演算子view$ : レンダリングを最適化するために、エンジンはそれらをレンダリングする前にすべての子供から最初の放出を待ちます.それで、我々がstartWith<div>view$ 値を出力します.したがって、私たちはstartWith 操作子や静的な子を持つ子をラップする場合など.<span>{ view$ }</span>と従来のカウンタを使った例です.
function CounterApp() {
  const input$ = new Subject();
  const view$  = input$.pipe(
      startWith(0),
      scan((acc, curr) => acc + curr)
    );

  return <div>
    <button onClick={ ()=>input$.next(-1) }>minus</button>
    { view$ }
    <button onClick={ ()=>input$.next( 1) }>plus</button>
  </div>
}
run the counter example
この例ではinput$ 我々はアップデートをプッシュします.The view$ 観測可能であるinput$ 使用scan 演算子と我々の状態が表示されます.例えばプッシュするとき1, 1, 1input$ — 私たちは1, 2, 3view$ .

REFSまたは「本当のDOM取引」


時々DOM APIと対話する必要があります.そのためにuses スペシャルref 現在のDOM要素への参照を含むオブジェクトcurrent プロパティー
// A React component
function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    inputEl.current.focus(); // `current` points to the mounted text input element
  };

  return (
    <div>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    <div/>
  );
}
もちろん、このフレームワークでは、DOM参照のストリームを取得!DOM要素が作成されるか置き換えられると - エンジンはストリームに新しい参照をプッシュします.私たちは、エンジンをエンジンに提供するために、参照のための場所を - エーSubject . それが本物のDOMに付けられるならば、エンジンはHTML要素をそれに押します.したがって、我々はストリームのHTMLElements そして、各更新プログラムまたは最新の参照に我々のロジックを適用することができます.
ここで我々はフォーカス<input /> 毎回<button/> クリックします.
// This framework
function TextInputWithFocusButton() {
  const ref$    = new Subject();
  const clicks$ = new Subject();

  clicks$
    .pipe(withLatestFrom(ref$, (_, ref) => ref))
    .subscribe(ref => {
      ref.focus();
    });

  return (
    <div>
      <input ref={ref$} type="text" />
      <button onClick={ ()=>clicks$.next(null) }>Focus the input</button>
    </div>
  );
}
run the refs example

サブコンポーネント


これまでのところ、我々は観測可能な結果を返すだけのコンポーネントを持ち、どんな入力にも反応する必要はなかった.子コンポーネントにプロパティーを提供する親コンポーネントの例を示します
import { timer } from 'rxjs';
import { map } from 'rxjs/operators';


function Parent () {
  return <div>{
    timer(0, 1000).pipe(
      map(i => <Child index={i} />)
    )
  }</div>
}

function Child (props$) {
  const animal$ = props$.pipe(
    map(props => props.index % 2 ? '🐱' : '🐭')
  )

  return <h1 style="text-align: center;">{animal$}</h1>
}
run the cat-mouse example
Parent レンダリングChild 初めて-それはレンダリングです<Child index={ 0 } /> . エンジンはChild を押すと{ index: 0 } サブコンポーネントのオブジェクトへのプロップprops$ 観測可能.子はすぐにマウスと反応する🐭.
後でtimer 再びダニを鳴らす<Child index={ 1 } /> — エンジンはプッシュするだけです{ index: 1 }Child props$ .
The Child 猫を作る🐱.
など.

によって生成される可能性のある猫の例Michael Sum on Unsplash

リドゥ


より大きなアプリケーションのために、我々は少しの洗練された州管理を必要とします、そして、ちょうど束の主題.観測可能な方法で出力するどんな実装も、recksで働きます!試しましょうredogs ステートマネージャーredux , redux-observable and typesafe-actions つの小さなパッケージで.我々は簡単に統合されますので、観察可能な出力にリドッグ!
革新的であり、例としてリストアプリケーションを行うにはシンプルな作成🙂
まず、ストアを作成します.
import { createStore } from 'redogs';
import { reducer } from './reducer';
import { effects } from './effects';

export const store = createStore(reducer, effects);
ここで、コンポーネントのストアの状態変化にアクセスできます.
import { store } from './store';

function ItemListComponent() {
  const items$ = store.state$.pipe(
    map(state =>
      state.items.map(item => (
        <ItemComponent key={item.id} data={item} />
      ))
    )
  );

  return <div>{items$}</div>;
}
またはイベントをディスパッチする
import { store } from './store';

function AddItemComponent() {
  const addItem = event => {
    event.preventDefault();
    const input = event.target['title'];
    store.dispatch(
      addItemAction({
        title: input.value
      })
    );
    input.value = '';
  };

  return (
    <form onSubmit={addItem}>
      <input name="title" type="text" autocomplete="off" />
      <button type="submit">Add</button>
    </form>
  );
}
簡潔さのために、私はここで還元剤、影響と他の構成要素を示してスキップします.どうぞご覧下さいredux app example at codesandbox .
我々は学ぶ必要はないことに注意してくださいreselect and re-reselect APIはReduxと対話する.
我々は、登録商標を微調整する必要はありませんstatic getDerivedStateFromProps() または心配するUNSAFE_componentWillReceiveProps() and UNSAFE_componentWillUpdate() フレームワークに効率的です.
私たちはオブザーバーブルを知る必要があるだけです.

反応しない


反応コンポーネントの自己更新をトリガするために-それはその状態や小道具を更新する必要があります.反応自体が再コンポーネントをレンダリングするときに決定されます.あなたが不必要な再計算と再レンダリングを防止したいならば、いくつかのAPIメソッド(またはフック)があります.
このフレームワークでは、このフローをより透明で調整可能にしたいと思います.入力ストリームに基づいて出力ストリームを直接操作します.filter , debounce, throttle, audit, sample , scan , buffer and many - many その他
いつ、どのようにあなたのコンポーネントを更新するかを決定!

ステータス


ソースコードはgithub.com/recksjs/recks
フレームワークを試すには、次のいずれかです.
  • それを実行するan online sandbox
  • または、以下のようにしてテンプレートリポジトリをクローン化できます.
  • git clone --depth=1 https://github.com/recksjs/recks-starter-project.git
    cd recks-starter-project
    npm i
    npm start
    
    パッケージも利用可能ですnpm i recks , 必要なのは、あなたのJSXトランスポーター(Babel、TypeScriptコンパイラ)を使用するように設定することですRecks.createElement プラグマ
    [警告]これはコンセプトです.

    免責事項


    まず第一に、私はこのライブラリを「フレームワーク」と呼びました.それで、人はそれを「ツール」または「図書館」と呼ぶのを好むかもしれません.君次第だ🙂
    また、反応する私の比較は純粋に概念的です.反応は、素晴らしいコミュニティに囲まれた専門家のスマートチームによってサポートさ成熟したフレームワークです.
    これは私によって建てられた1週間です🐶

    代替案


    オブザーバブルと対話するために反応フックを提供するライブラリが一つあります.rxjs-hooks . それはuseState フックは、コンポーネントが再表示するたびにコンポーネントの状態を更新します.チェックアウト価値!
    ここで言及するもう一つの象は、本当のストリーム駆動のフレームワークです:cycle.js によって.それには多くの支持者と固い統合があります.サイクル.jsは、サブコンポーネントを使用して、DOMと相互作用する少しのAPIを持っています.試してみてください!
    あなたが他の選択肢を知っているならば

    アウトロ


    わかりました.
    このプロジェクト開発は続きますか?
    どのような機能を次のを見たいですか?
    私はあなたの考えを知っているので、コメントを残してください🙂
    あなたがこの記事を読んで楽しんだ場合-“ハート”と共有を押してください:これは私はこのトピックの有用性を理解し、他の人がこの読み取りを発見する助けとなるでしょう.
    次のポストでは、他のrecksの統合を確認し、私は機能の計画を共有し、プロジェクトの更新を発行します.だからここで私に従って、チューニングを維持する!
    あなたがこれまで読んだことを誇りに思います!
    ありがとう

    終わり


    ヘッダー写真Matthew Smith on Unsplash