React: 横向きのデータ読み込みを実装する

作成日 2015年03月13日  ·  136コメント  ·  ソース: facebook/react

これは、グローバルストア/ネットワーク/リソースからステートレス(メモ化されている可能性があります)データを横方向にデータロードするためのファーストクラスのAPIであり、入力として小道具/ステートを使用する可能性があります。

type RecordOfObservables = { [key:string]: Observable<mixed> };

class Foo {

  observe(): RecordOfObservables {
    return {
      myContent: xhr(this.props.url)
    };
  }

  render() {
    var myContent : ?string = this.data.myContent;
    return <div>{myContent}</div>;
  }

}

observe()は、componentWillMount / componentWillUpdateの後、レンダリングの前に実行されます。

レコード内のキー/値ごと。 値でObservableをサブスクライブします。

subscription = observable.subscribe({ onNext: handleNext });

onNextをサブスクライブから同期的に呼び出すことができます。 そうである場合は、次のように設定します。

this.data[key] = nextValue;

それ以外の場合は、最初のレンダリングでは未定義のままにしておきます。 (多分それをnullに設定しますか?)

その後、レンダリングは通常どおり続行されます。

onNextが呼び出されるたびに、このコンポーネントでforcedUpdateを効果的にトリガーする新しい「this.data [key]」をスケジュールします。 これが唯一の変更である場合、監視は再実行されません(componentWillUpdate-> render-> componentDidUpdate)。

小道具/状態が変更された場合(つまり、recievePropsまたはsetStateからの更新)、observe()が再実行されます(調整中)。

この時点で、新しいレコードをループし、すべての新しいObservableをサブスクライブします。

その後、以前のObservablesのサブスクライブを解除します。

subscription.dispose();

この順序付けは、データのプロバイダーがキャッシュの参照カウントを実行できるようにするために重要です。 つまり、誰も聞いていない限り、データをキャッシュできます。 すぐにサブスクライブを解除すると、同じデータを再度サブスクライブする前に、参照カウントがゼロになります。

コンポーネントがマウント解除されると、アクティブなすべてのサブスクリプションから自動的にサブスクリプションが解除されます。

新しいサブスクリプションがすぐにonNextを呼び出さなかった場合は、以前の値を引き続き使用します。

したがって、例のthis.props.urlが変更され、新しいURLをサブスクライブしている場合、myContentは、次のURLが完全に読み込まれるまで、前のURLのコンテンツを表示し続けます。

これは、 <img />タグと同じセマンティクスを持っています。 これは混乱を招き、不整合につながる可能性がありますが、かなり正常なデフォルトであり、反対のデフォルトを使用するよりもスピナーを表示する方が簡単です。

データがキャッシュされていない場合は、すぐに「null」値を送信することをお勧めします。 もう1つの方法は、ObservableがURL(またはID)とコンテンツの両方を結果に提供することです。

class Foo {

  observe() {
    return {
      user: loadUser(this.props.userID)
    };
  }

  render() {
    if (this.data.user.id !== this.props.userID) {
      // Ensure that we never show inconsistent userID / user.name combinations.
      return <Spinner />;
    }
    return <div>Hello, {this.data.user.name} [{this.props.userID}]!</div>;
  }

}

ObservableのRxJSコントラクトを使用する必要があります。これは、より一般的に使用され、同期実行が可能になるためですが、 @ jhusainの提案がより一般的に使用されるようになったら、代わりにそのコントラクトに切り替えます。

var subscription = observable.subscribe({ onNext, onError, onCompleted });
subscription.dispose();

必要に応じて、これらのイベントに応答するライフサイクルフックを追加できます。

注:この概念により、横向きのデータを「動作」のように、つまり小道具のように動作させることができます。 これは、これらのものの概念状態をオーバーロードする必要がないことを意味します。 これにより、後で再サブスクライブするためだけにデータを破棄するなどの最適化が可能になります。 復元可能です。

Component API Big Picture

最も参考になるコメント

誰かがこの種のAPIを試してみたい場合は、高階コンポーネントとしてobserve非常に馬鹿げたポリフィルを作成しました。

import React, { Component } from 'react';

export default function polyfillObserve(ComposedComponent, observe) {
  const Enhancer = class extends Component {
    constructor(props, context) {
      super(props, context);

      this.subscriptions = {};
      this.state = { data: {} };

      this.resubscribe(props, context);
    }

    componentWillReceiveProps(props, context) {
      this.resubscribe(props, context);
    }

    componentWillUnmount() {
      this.unsubscribe();
    }

    resubscribe(props, context) {
      const newObservables = observe(props, context);
      const newSubscriptions = {};

      for (let key in newObservables) {
        newSubscriptions[key] = newObservables[key].subscribe({
          onNext: (value) => {
            this.state.data[key] = value;
            this.setState({ data: this.state.data });
          },
          onError: () => {},
          onCompleted: () => {}
        });
      }

      this.unsubscribe();
      this.subscriptions = newSubscriptions;
    }

    unsubscribe() {
      for (let key in this.subscriptions) {
        if (this.subscriptions.hasOwnProperty(key)) {
          this.subscriptions[key].dispose();
        }
      }

      this.subscriptions = {};
    }

    render() {
      return <ComposedComponent {...this.props} data={this.state.data} />;
    }
  };

  Enhancer.propTypes = ComposedComponent.propTypes;
  Enhancer.contextTypes = ComposedComponent.contextTypes;

  return Enhancer;
}

使用法:

// can't put this on component but this is good enough for playing
function observe(props, context) {
  return {
    yourStuff: observeYourStuff(props)
  };
}

class YourComponent extends Component {
  render() {
    // Note: this.props.data, not this.data
    return <div>{this.props.data.yourStuff}</div>;
  }
}

export default polyfillObserve(YourComponent, observe);

全てのコメント136件

undefinedは、observableがonNext介して最初の値を提供するまで、おそらくdataに割り当てるのに最も安全な値です。 たとえば、Relayでは、 null (データが存在しない)とundefined (まだフェッチされていない)に異なる意味を割り当てるため、理想的なデフォルトのデータ値はundefinedます。 別の方法は、新しい方法、たとえばgetInitialDataを提供することですが、これは不要/やり過ぎだと思います。

これは非常に興味深いものですが、静的型付けの観点からは、キー/値システムにあまり満足していません。それらの型を表現することはほとんど不可能です。
observeが単一のオブザーバブルを返し、解決された値をthis.data設定/マージしないのはなぜですか?

class Foo {

  observe() {
    return (
      loadUser(this.props.userID)
        .map(user => { user })
  }

  render() {
    if (this.data.user.id !== this.props.userID) {
      // Ensure that we never show inconsistent userID / user.name combinations.
      return <Spinner />;
    }
    return <div>Hello, {this.data.user.name} [{this.props.userID}]!</div>;
  }

}

そして、複数のフェッチの場合は次のようになります。

class Foo {

  observe() {
    return (
     combineLatest(
      loadUser(this.props.userID),
      loadSomethingElse(this.props.somethingElseId),
      (user, somethingElse) => ({ user, somethingElse})
     )
  }

  render() {
    ..
  }

}

これはおそらくもう少し冗長ですが、それは素晴らしい静的型を持つことを可能にします:

interface Comp<T> {
  observe(): Observable<T>;
  data: T;
}

また、props / stateの変更時にobserveを再実行する代わりに、監視可能なものとして 'props''state'にアクセスできます。

class Foo {

  observe(propsStream) {
    return (
      propsStream
        .flatMap(({ userID }) => loadUser(userId))
        .map(user => { user })
    );
  }

  render() {
    if (this.data.user.id !== this.props.userID) {
      // Ensure that we never show inconsistent userID / user.name combinations.
      return <Spinner />;
    }
    return <div>Hello, {this.data.user.name} [{this.props.userID}]!</div>;
  }
}

その理由は、(複数の)Observableをサブスクライブできるようにするために、コンビネーターの使用とRxJSの理解を必要としないためです。 このように2つのオブザーバブルを組み合わせると、かなり混乱します。 実際、少なくともデータソースについては、サブスクリプションAPIを実装する可能性がありますが、Observablesのプロトタイプにコンビネーターを含めることすらありません。 これは必須ではありませんが、必要に応じてコンビネータを自由に使用できます。

ただし、単純なFluxストアにサブスクライブするために、サブスクライブする必要はありません。

Flowはおそらく制約を使用してこの静的タイプを処理できると思いますが、確認するためにそれらの人に確認します。 dataプロパティを入力するだけで十分だと思います。そうすれば、observe型を暗示することができます。

class Foo extends React.Component {
  data : { user : User, content : string };
  observe() /* implied as { user : Observable<User>, content : Observable<string> } */ {
  }
}

この変更は、アプリケーションの状態を説明する方法としてObservablesに全面的に取り組むことではありません。 これは、以前と同じように、これに加えて実装できます。 このメソッドはべき等であるため、これは明らかにアプリケーションの状態に関するものではありません。 フレームワークは、必要に応じてサブスクライブを解除し、再サブスクライブすることができます。

cc @ericvicenti

少なくともtypescriptの場合、少なくともhttps://github.com/のようなものになるまで、 dataのタイプに基づいてobserveのリターンタイプを制約する方法はありません。 Microsoft / TypeScript / issues / 1295が実装されています。

これをReactDnDの次のバージョンで使用したいのですが、明らかにこれにはReact0.14を待つ必要があります。
refインスタンスにthis.dataを設定する高階コンポーネントを使用して、当面これを「ポリフィル」できるかどうか疑問に思います。

約束を守ることは可能でしょうか? 次に、Promisesツリーを使用して、最初のレンダリングの前にコンポーネントツリー全体のデータを解決できます。 これはサーバー側のReactに非常に役立ちます。

これをファーストクラスのAPIにすることの利点は何ですか? これは、基本的に「高次コンポーネント」を使用して実現できます。

これをファーストクラスのAPIにすることの利点は何ですか? これは、基本的に「高次コンポーネント」を使用して実現できます。

5つのHOCでラップして5つのサブスクリプションを取得するのは少し扱いに​​くく、初心者にとっては理解しにくいものです。 componentWillReceiveProps理解することも簡単ではありません。 これで両方が修正されます。

私は、一つには、私たちの新しい観察可能な大君主を歓迎します。

これがhttps://github.com/chenglou/react-state-streamをReactのバニラAPIに近づけるのに役立つのではないかと思います

1つのHOCだけでいいのではないでしょうか。 Medium投稿の例ではstoresを繰り返し処理し、それぞれにサブスクライブします。

stores.forEach(store =>
  store.addChangeListener(this.handleStoresChanged)
);

@aaronshaf確かに、ユースケースによって異なります。 「いくつかの店舗」だけでなく、さまざまな種類の州の情報源である場合もあります。 しかし、Reactチームを代表して言うことはできません。 @ sebmarkbageの言うことを聞いてみましょう。

今これで遊ぶためにある種のポリフィルが大好きです。 私はまだその考えを完全には理解していませんでした。 将来の更新の処理に関連するメカニズムは何ですか。 私はそれを理解するためにもう少し時間を費やします。 単純なミックスインで実行できるはずだと思います。

@vjeuxは私にチャイムを鳴らすべきだと言った!だからここにいる。)

自分の作品を宣伝するつもりはありませんが、このフックはReact NexusgetNexusBindingsフックと非常に似ていると思います。 ライフサイクルフック(小道具に依存する可能性があります)を介して、コンポーネントレベルでデータデップを宣言します。

APIは次のようになります。

class UserDetails {
  getNexusBindings(props) {
    return {
      // binding to data in the datacenter
      posts: [this.getNexus().remote, `users/${this.props.userId}/posts`],
      // binding to data in the local flux
      mySession: [this.getNexus().local, `session`],
    }
  }
}

バインディングは、 componentDidMountおよびcomponentWillReceivePropsの間に適用/更新されます。 後者の場合、次のバインディングは前のバインディングとは異なります。 削除されたバインディングはサブスクライブ解除され、追加されたバインディングはサブスクライブされます。 基盤となるフェッチ/更新メカニズムは、 NexusFluxの実装で説明されています。 基本的に同じAPIを使用して、ローカルデータ(従来のローカルストア)またはリモートデータ(GETを使用してフェッチし、Websocket / polyfillを介してパッチを受信する)をサブスクライブできます。 実際には、別のウィンドウ(postWindowを使用)またはWebWorker / ServiceWorkerからデータをサブスクライブすることもできますが、これに本当に役立つユースケースはまだ見つかりません。

簡単に言うと、Flux抽象化を使用して、コンポーネントレベルでデータの深さを同期的に記述し、フックによって、依存関係が自動的にサブスクライブされ、更新時に注入され、サブスクライブ解除されるようにします。

ただし、これには優れた機能もあります。まったく同じライフサイクル機能を利用して、サーバー側のレンダリング時にデータのプリフェッチを実行します。 基本的に、ルートから開始し、そこから回復的に、React Nexusはバインディングをプリフェッチし、コンポーネントをレンダリングし、すべてのコンポーネントがレンダリングされるまで子孫を続行します。

@ aaronshaf @ gaearonファーストクラスにすることの利点は次のとおりです。

1)小道具の名前空間を食い尽くしません。 たとえば、高次コンポーネントは、他の用途に使用できないpropsオブジェクトからdataのような名前を要求する必要はありません。 複数の高次コンポーネントをチェーンすると、より多くの名前が消費され続けるため、これらの名前を一意に保つ方法を見つける必要があります。 すでに作成されている可能性のあるものを作成していて、名前が競合している場合はどうなりますか?

また、高次コンポーネントのベストプラクティスは、ラップされたコンポーネントのコントラクトを変更しないようにすることだと思います。 つまり、概念的には、アウトと同じ小道具である必要があります。 そうしないと、消費者が受け取ったものとはまったく異なる小道具のセットを提供するときに、使用とデバッグが混乱します。

2)最後の値を格納するためにstateを使用する必要はありません。 dataの概念は、純粋にメモ化であるという意味でpropsに似ています。 メモリを再利用する必要がある場合は、いつでも自由に破棄できます。 たとえば、無限スクロールでは、非表示のサブツリーを自動的にクリーンアップする場合があります。

@RickWongはい、 Promiseをサポートするのはかなり簡単です。 私たちはおそらく、ピニオンを外すためにそれを行うべきです。 ただし、それでもおそらく使用しないことをお勧めします。 次の理由により、Observablesより劣っています。

A)フレームワークによって自動的にキャンセルすることはできません。 私たちにできる最善のことは、遅い解決策を無視することです。 その間、Promiseは潜在的に高価なリソースを保持します。 長時間実行されているタイマー/ネットワークリクエストのサブスクライブ/キャンセル/サブスクライブ/キャンセル...のスラッシュな状況に陥るのは簡単です。Promisesを使用すると、ルートでキャンセルされないため、待つ必要があります。完了するまたはタイムアウトするリソース。 これは、大きなデスクトップページ(facebook.comなど)のパフォーマンスや、メモリに制約のある環境(react-nativeなど)のレイテンシークリティカルなアプリに悪影響を与える可能性があります。

B)単一の値のみを取得するように自分自身をロックしています。 そのデータが時間の経過とともに変化する場合、ビューを無効にすることはできず、一貫性のない状態になります。 反応性ではありません。 ただし、単一のサーバー側レンダリングでは問題ない場合がありますが、クライアントでは、新しいデータをUIにストリーミングし、古いデータを回避するために自動的に更新できるように設計するのが理想的です。

したがって、Observableは、必要に応じてこれらの問題を修正するためにロックインしないため、構築するのに優れたAPIであることがわかりました。

@elierotenbergチャイムをしてくれてありがとう! それは確かに非常に似ているようです。 同じ種類の利点。 私の提案に何か制限はありますか? つまり、React Nexusに欠けているものがありますが、これの上に構築することはできませんでしたか? 重要なユースケースから自分自身を締め出さなければ、いいでしょう。 :)

サーバーレンダリングの観点からは、非同期でフェッチできるデータでObservable / Promiseが解決されるまで、最終的なrenderToStringを延期できることが重要です。 それ以外の場合は、ページにどのコンポーネントが含まれるかを知らなくても、Reactの外部ですべての非同期データフェッチを実行する必要があります。

私は、react-nexusを使用すると、レンダリングツリーを続行する前に、コンポーネント内で非同期ロードを実行できると思います。

はい、react-nexusは明示的に分離します:
1) getNexusBindingsとしてのバインディング宣言(これは、renderに似た、同期の副作用のないライフサイクルメソッドです。実際にはrenderDependenciesという名前でしたが、混乱を招くと思いました)、
2)サブスクリプション/更新をapplyNexusBindingsとしてバインドします(これは同期であり、以前のネクサスバインディングを比較して、サブスクライブする必要がある新しいバインディングとサブスクライブ解除する必要があるバインディングを決定します)
3)プリフェッチをprefetchNexusBindingsとしてバインドします(これは非同期であり、「初期」(これが意味するものは何でも)値の準備ができたときに解決されます)

ReactNexus.prefetchApp(ReactElement)Promise(String html, Object serializableData)返します。 このフックは、Reactツリーの構築を模倣し( instantiateReactComponent )、コンポーネントを再帰的に構築/プリフェッチ/レンダリングします。 コンポーネントツリー全体が「準備完了」になると、すべてのデータの準備ができていることを認識して、最終的にReact.renderToString呼び出します(モジュロエラー)。 解決されると、このPromiseの値をサーバーの応答に挿入できます。 クライアントでは、通常のReact.render()ライフサイクルは通常どおり機能します。

誰かがこの種のAPIを試してみたい場合は、高階コンポーネントとしてobserve非常に馬鹿げたポリフィルを作成しました。

import React, { Component } from 'react';

export default function polyfillObserve(ComposedComponent, observe) {
  const Enhancer = class extends Component {
    constructor(props, context) {
      super(props, context);

      this.subscriptions = {};
      this.state = { data: {} };

      this.resubscribe(props, context);
    }

    componentWillReceiveProps(props, context) {
      this.resubscribe(props, context);
    }

    componentWillUnmount() {
      this.unsubscribe();
    }

    resubscribe(props, context) {
      const newObservables = observe(props, context);
      const newSubscriptions = {};

      for (let key in newObservables) {
        newSubscriptions[key] = newObservables[key].subscribe({
          onNext: (value) => {
            this.state.data[key] = value;
            this.setState({ data: this.state.data });
          },
          onError: () => {},
          onCompleted: () => {}
        });
      }

      this.unsubscribe();
      this.subscriptions = newSubscriptions;
    }

    unsubscribe() {
      for (let key in this.subscriptions) {
        if (this.subscriptions.hasOwnProperty(key)) {
          this.subscriptions[key].dispose();
        }
      }

      this.subscriptions = {};
    }

    render() {
      return <ComposedComponent {...this.props} data={this.state.data} />;
    }
  };

  Enhancer.propTypes = ComposedComponent.propTypes;
  Enhancer.contextTypes = ComposedComponent.contextTypes;

  return Enhancer;
}

使用法:

// can't put this on component but this is good enough for playing
function observe(props, context) {
  return {
    yourStuff: observeYourStuff(props)
  };
}

class YourComponent extends Component {
  render() {
    // Note: this.props.data, not this.data
    return <div>{this.props.data.yourStuff}</div>;
  }
}

export default polyfillObserve(YourComponent, observe);

Observableは具体的で、ライブラリの実装以外で合意されたものですか? 契約とは何ですか、ベーコンやRxjsを使用せずに実装するのは簡単ですか? データをサイドローディングするためのファーストクラスのAPIは素晴らしいですが、Reactがプレーンなjsに向かって着実に動いていることを考えると、Reactが未仕様/非常に初期仕様のプリミティブに基づいてAPIを追加するのは奇妙に思えます。 このようなものは、特定のユーザーの土地の実装に私たちを結び付けますか?

余談ですが、なぜStreamsではないのですか? レースには馬がいませんが、正直なところ疑問に思っています。 Webストリームですでに作業が行われており、もちろんノードがあります

考慮すべきもう2つの例: https ://github.com/cujojs/mostとhttps://github.com/caolan/highland

@jquense ObservableをECMAScript7(+)に追加する提案について活発な作業が行われているため、理想的にはこれがプレーンなJSになります。 https://github.com/jhusain/asyncgenerator (現在は古くなっています)

RxJSへの依存はありません。 APIは、RxJSを使用せずに自分で実装するのは簡単です。 RxJSは、アクティブなECMAScriptプロポーザルに最も近いものです。

most.jsも実行可能のようです。

Bacon.jsのAPIは、値を区切るためにBacon.Eventタイプを使用しているため、Baconに依存せずに使用するのは難しいようです。

Stream APIは高レベルであり、このユースケースからはほど遠いものです。

ある種の「レンダリング前に待機」オプションはありますか?クライアントでは、レンダリング前にすべてのObservableを待機する必要はありませんが、サーバーでは、各コンポーネントのrender()が解決されるようにそれらが解決されるのを待機する必要があります。部分ではなく、

[parent] await observe(). full render(). -> [foreach child] await observe(). full render().

私のすべての調査で、これがサーバー側のReactに欠けている最も重要なライフサイクルフックである

この議論に続いて、私はReactNexusが何をするかを次の投稿で要約しようとしました:

ReactNexusで正しく行われたIsmorphicアプリ

コアプリフェッチルーチンの図は次のとおりです。

React Nexus

RxJSへの依存はありません。 APIは、RxJSを使用せずに自分で実装するのは簡単です。 RxJSは、アクティブなECMAScriptプロポーザルに最も近いものです。

:+1:これは私にとって大きな懸念事項です。たとえば、自分が何をしているのかを理解していない限り、自分で実装するのは非常に困難です。 そうしないと、エコシステム内の特定のライブラリに対する暗黙の要件になってしまうと思います。 正直なところ... promiseの世界の優れた点の1つはA +テストスイートです。したがって、ライブラリ間でさえ、少なくとも.then共通機能が保証されていました。これは、promiseの相互運用に役立ちました。標準化。

これは私にとって大きな懸念事項です。たとえば、自分が何をしているのかを知らない限り、自分で実装するのは非常に難しいという約束です。 そうしないと、エコシステム内の特定のライブラリに対する暗黙の要件になってしまうと思います。

完全に同意しました。 ありがたいことに、オブザーバブルのコントラクトは非常に単純であり、 thenような組み込みメソッドすら持っていないため、約束よりもさらに単純です。

委員会がnextを呼び出すと、Promisesのようなマイクロタスクがスケジュールされると主張する場合、それらはより複雑になる(そして遅くなる)可能性があります。

onNextがRxJSで同期しているという事実に基づいているため、多くのパターンが気になります:/

一般的なFluxストアのパターンは、再利用できるように、キーごとにObservablesのマップを保持することだと思います。 次に、全員が登録解除されたら、それらをクリーンアップします。

そうすれば、次のようなことができます: MyStore.get(this.props.someID)そして常に同じObservableを取り戻す。

そうすれば、MyStore.get(this.props.someID)のようなことができ、常に同じObservableを取り戻すことができます。

this.props.key (私が知っている)を使用することは意味がありますか? ほとんどの場合、すでにそのような一意の識別子を<... key={child.id} .. />渡します。

そうすれば、MyStore.get(this.props.someID)のようなことができ、常に同じObservableを取り戻すことができます。

それは私がReactNexusにも使用しているパターンです。 Store#observeは、メモ化された不変のオブザーバーを返します。 すべてのサブスクライバーが少なくとも1ティック離れると、クリーンアップされます(実際の「サブスクライブ解除」メッセージの送信など、関連するバックエンド固有のクリーンアップメカニズムを含む)。

@sebmarkbage @gaearon v0.14のサーバーでの動作をどのように観察しますか?
すべてのオブザーバーが解決するのを適切に待ってから、react-nexusが行うのと同じように文字列にレンダリングできますか(ただし、反応するように組み込まれています)?

IMOコンポーネントがサーバーでのレンダリングの「準備ができている」前に、最初の観測値を待っていれば素晴らしいと思います。

@gaearon :IMOコンポーネントがサーバーでのレンダリングの「準備ができている」前に、最初の観測値を待っていれば素晴らしいと思います。

はい、:+ 1:非同期レンダリングの場合。 それまでの間、 @ andreypoppによるreact-asyncは代替手段ですが、Reactを「ハック」するにはfibersが必要です。 Reactがすぐに使える非同期レンダリングをサポートできれば素晴らしいと思います。

非同期レンダリングはサポートしたいものですが、この問題の一部ではありません。 L

残念ながら、おそらく0.14では達成できません。 検討およびリファクタリングするための多くの異なる設計が必要です。

それを実現するために必要な内部のアーキテクチャ上の変更を説明するために、自由に作成して発行してください。

私は@gaearonrereact-streaming-stateと同じ考えを持っていました。 サイドローディング以外のすべての潜在的なアプリケーションを考えると、 dataよりも良い名前があるでしょうか? たとえば、 observedは、メソッドとより明確に関連付けます。

バイクシェディングで脱線するつもりはありませんが、これを捨てたかったのです。

Reactでオブザーバブルを待つことはできません。 私が理解しているように、これはReactを反応させるはずです

react-asyncを書き直しながら、同様のアイデアを試しています。READMEを参照してください。

注目すべき違いは、Reactがkeyプロップとステートフルコンポーネントで行うのと同様に、プロセスを調整するために明示的な監視可能/プロセスIDを導入することです。

名前付きプロセスのidが変更されると、React Asyncは古いプロセスインスタンスを停止し、新しいプロセスインスタンスを開始します。

APIは次のようになります。

import React from 'react';
import Async from 'react-async';

function defineFetchProcess(url) {
  return {
    id: url,
    start() {
      return fetch(url)
    }
  }
}

function MyComponentProcesses(props) {
  return {
    user: defineFetchProcess(`/api/user?user${props.userID}`)
  }
}

@Async(MyComponentProcesses)
class MyComponent extends React.Component {

  render() {
    let {user} = this.props
    ...
  }
}

プロセスAPIは、構文的にも名前的にもES6 Promises APIに従いますが、意味的には、プロセスのライブごとに1回だけprocess.then(onNext, onError)が呼び出されることは想定されていません。 これは、promiseを介してデータをフェッチする最も一般的な(?)ユースケースに対応するために作成されています。 でも正直なところ、混乱を防ぐために変更する必要があると思います。

私は間違っているかもしれませんが、提案された(この問題で)APIをユーザーランドに実装することを妨げる唯一のことは、レンダリングの直前にcomponentWillUpdate 、新しいpropsで実行されるライフサイクルフックがないことだと思いますstateすでにインスタンスにインストールされています。

まだ説明されていないことの1つは、 onErrorコールバックの処理です。 オブザーバブルがエラーを生成する場合、その情報は何らかの方法でコンポーネントに利用可能である必要があります。 Reactは実際のsubscribe(callbacks)呼び出しを処理するため、そのコールバックオブジェクトに注入するための標準化されたメソッドが必要になります。

アプリケーション開発者に最も柔軟性を提供するために、2つのアプローチがあります。 1つ目は、 this.dataと同様に、エラーを最上位の属性に配置することです。 これは非常に手間がかかるようで、コンポーネントの名前空間をさらに使い果たします。
2つ目は、開発者が独自のonErrorコールバックをライフサイクル風の関数として定義できるようにするものです。 オブザーバブルのカスタムエラー処理が必要な場合は、次のようなものを追加できます。

onObserveError(key, error) {
  // do something with the error
  this.state.errors[key] = error;
  this.setState({ errors: this.state.errors });
}

これは、Parse + Reactの次の反復で行ったことと似ています。 必要なAPIを生成するために、独自のエラー処理を作成しました。 エラーはプライベート{name => error}マップに追加され、コンポーネントには、マップが空でない場合にマップのクローンを返すトップレベルのパブリックメソッドqueryErrors()nullがあります。それ以外の場合(単純なif (this.queryErrors())を許可します。

もちろん、新しい予約済みメソッドを定義することも難しいビジネスです。 私はそうではないふりをしません。 ただし、レンダリング時にコンポーネントがエラーを暗黙的または明示的に利用できるようにする方法が必要です。

@andrewimmアイデアは、エラー境界によって処理されるまで、エラーを階層の上位にバブルする一般的なエラー伝播システムにフィードすることです。 https://github.com/facebook/react/issues/2928

これは、メソッドのスローと正常な回復のエラーも処理する必要があります。 render()メソッドがスローする場合のように。 これはかなり手間がかかり、適切に実装するには時間がかかりますが、この方法でエラー処理を統合するというアイデアがありました。

これはreactproperの外に残しておくべきであり、1つまたは2つの主要な統合ポイントをreact-asyncやreact-nexusなどのプロジェクトと調整して、Reactproperの上できれいに実行できるようにする必要があると私は主張します。

私は同意します、これを行うための提案された方法を持っていることはより良いようです
これをフレームワーク自体に焼き付けます

2015年4月21日火曜日、午後11時38分Rodolfo [email protected]
書きました:

私はこれが適切な反応の外に残されるべきであり、1つまたは2つのキーであると主張します
統合ポイントは、react-asyncやreact-asyncなどのプロジェクトと調整する必要があります
これはReactの適切な上できれいに行うことができるようにreact-nexus ...


このメールに直接返信するか、GitHubで表示してください
https://github.com/facebook/react/issues/3398#issuecomment-95048028

週末に、 Flexyと呼ばれるさらに別のFlux実装を構築しました。 この中で、ストアのコードを確認してください。 オブザーバブルや他のリアクティブフレームワークが実際に使用されていない場合でも、オブザーバブルAPIに準拠する.getObservableメソッドを公開します。

したがって、APIは実際のObservablesで作成するのに十分簡単だと思います。

とはいえ、コードを厳しく判断しないでください。週末に次の目的で実行されました。

  • 楽しい
  • 理解
  • js-cspを使用する
  • 監視APIを使用する

ちなみに、このようなシステムとFluxを使用すると、実際にはサーバー側のレンダリングの負担が軽減されます。 React-Nexusと同様のシステムを使用して、ストアを初期化し、Reactアプリに渡すことができます。 その後、ストアとディスパッチャを監視し、アクションが実行されなくなるまで再レンダリングを続けることができます(必要なデータはすべてストアにすでに存在します)。

これは、ステートレスデータサブスクリプションの新しいセマンティクスを取得するための最小の統合ポイントであると私は主張します。 他にどのような統合ポイントを提案しますか? はるかに複雑な問題であり、独自のスレッドに値する非同期レンダリングは含まれていません。このフックは、非同期レンダリングで使用できます。

他のすべては、コンポーネントごとにReactの上に実装することがすでに可能です。 プラグインのグローバルインジェクションは行わないことに注意してください。これは、環境全体でのコンポーネントの再利用を妨げるため、React上に構築されたフレームワークは個々のコンポーネントにコンテキストする必要があります。

どのフックが欠けていますか?

おい、

正直なところ、新しいフックを使用すると実装が簡単になりますが、 react-asyncreact-nexus示したように、フックがなくても横向きのデータ読み込みを確実に実現できます。

どちらかといえば、マウントされたReact階層の外でReactコンポーネントインスタンスのライフサイクルを維持することを公開してサポートすることは役に立ちます。 react-nexusでは、内部のinstanciateReactComponent react-nexusを使用して、 componentWillMountcomponentWillUnmountなどを自分で呼び出し、このアプローチは脆弱だと考えています( instanciateReactComponents場合instanciateReactComponentsは、Reactの次のバージョンで変更される内部不変条件に依存していますか?)。

ステートレスアプローチに関しては、非同期データがステートフルであるため、一部のコンポーネントの状態で保留/完了/失敗ステータスを保存することが適切であるように思われます。 実際のアプリでreact-nexusを使用すると、データフェッチを実行し、フェッチ状態を子コンポーネントに小道具として注入する高次コンポーネントがあります。 したがって、内側のコンポーネントは「ステートレス」(望ましい)であり、外側のコンポーネントは「ステートフル」(たとえば、ローディングスピナーまたはプレースホルダーを表示するためにも望ましい)です。

他にどのような統合ポイントを提案しますか?

@ANDREYPOPPが正しい質問をしたように私にはdataを変更するときに、forceUpdateを適切に設定およびトリガーします。 私がそれについて何か特別なものを見逃していない限り(完全に可能です)?

私はここでより大きな議論に引き込まれていません。
しかし、 @ sebmarkbageに答える

  • オブザーバブルによってプッシュされた値をデータに設定する前に処理できる関数を提供できるフック。 実際のオブザーバブルでは、これは.map

状況がもう少しオープンであれば、Observable固有の動作をカスタムフックに置き換えるフックがあるはずだと思います。 このようにして、代わりにイベントエミッターまたはCSPチャネルを使用できます。

最後のいくつかのコメントは、最小の拡張ポイントは実際にはライフサイクルフックの「接続」と「切断」であると言っているように思えます。これにより、非同期データをコンポーネントにアタッチして、それらのサブスクリプションを簡単に管理できますか?

オブザーバブルは、その上に(コアの内部または外部で)かなり簡単に構築できますが、これらのライフサイクルポイントを公開することで、より幅広い魅力がありますか?

それは合理的な要約ですか?

また、これはReact自体で解決する必要があるとはまだ確信していません。 私は、ReactがReactの上にこの機能を構築するために必要なフックを提供する必要があると考える傾向があります。

しかし、しばらくの間、 observe()を使用するとします。 いくつかの考え:

this.propsthis.statethis.context 、そして今ではthis.dataがあり、これらはすべてrender()の新しいデータの潜在的なソースです。 これは私には過剰に思えます。 アプリケーションの状態をコンポーネントの状態から分離するという考えはありますか? これにより、州に関するいくつかの問題が明確になり、分離される可能性がありますが、新しい入力を導入するコストが利益を上回らない可能性があると思います。 this.stateをコンポーネントの状態のみに集中させたい場合は、代わりにthis.dataのフィールドをthis.propsまたはthis.contextに追加してみませんか?

this.dataという名前は一般的すぎます。 小道具はデータであり、状態はデータであり、ローカル変数はデータです。 名前は意味を追加せず、既存の意味を混乱させます。 私はthis.observedまたは実際に何かを意味する他の名前をはるかに好みます。 したがって、 @ matthewwithanmのコメントに+1します。

dataよりも良い名前があるでしょうか? たとえば、 observedは、メソッドとより明確に関連付けます。

サーバーでobserve()を実行させる場合、アンマウントが発生することはないため、これによって発生する可能性のあるメモリリークをクリーンアップする何らかのフックが必要です。

propsまたはstateが変更されるたびに(およびcontext ?) observe()を再度呼び出す場合、 observeはパフォーマンスを最適化する必要があり、理想的には誤って高価にすることはできません。 ホットパスの一部になります。 私はReactNexusからこれが好きです:

次のバインディングは前のバインディングとは異なります。 削除されたバインディングはサブスクライブ解除され、追加されたバインディングはサブスクライブされます。

私は、状態がコンポーネントを複雑にしていると信じるようになり、自分のコンポーネントでの使用を減らし、代わりに持ち上げようとしています。 これが、 @ fisherwebdevが提起した懸念(私は今同意します)に加えて、 observestate $に依存させることが良い考えであると私が確信していない理由です。 状態は小道具に依存し、観察されるのは状態と小道具に依存します。複雑すぎませんか? observe(props)が欲しいです。

また、observeはstateに依存するべきではないことに気づきました。これは、主に、依存する必要がないためです。 @gaearonが指摘しているように、状態を1レベル上に上げるのは簡単です。これは、関心の分離後、よりクリーンに感じられます。 observestateに依存する可能性がある場合、コンポーネント内での更新の処理に関するロジックは非常に複雑になります。 propsのみに依存する場合は、 componentDidMount / componentWillReceivePropsフォークレスハンドラーを設定できます。 クリティカルパス内のコードが少ないほど、レンダリングサイクルが単純になり、サブスクリプションを再トリガーする意図しない更新の数も減ります。

監視(小道具)の+1

州との取引が少なければ少ないほど、IMOは優れています。

私は、ライブラリとして、Reactは可能な限り柔軟になるように努めるべきだと考えています。 観察するのは状態に依存するのは良い考えではないことに同意します。 はい、それは複雑になる可能性があります。 はい、ベストプラクティスは州に依存しないことです。
しかし、それはReactのユーザーが行うことを許可されるべき選択です。
現在のAPIを変更せずに(たとえば、柔軟性を追加するための可能なフックを除いて)、observeメソッドで状態を使用することは推奨されないことを説明するドキュメントを証明することをお勧めします。

誰もが正しいことをしたいと思っており、正しい方向に向けられたいと思っています。 状態を受け入れないことを観察すると、ドキュメントで「これはアンチパターンです」のようなものを誤って見つけるよりも、ユーザーにとって簡単になります。

編集:申し訳ありませんが、私がflatMapを探していたことに気づきました。 メッセージの配列をフラット化すると思っていたので混乱しましたが、より高いレベルで動作しています(メッセージは監視可能です)。

提案されたAPIは、あるデータフィールドの結果が別のデータフィールドの結果に依存している場合を処理しますか? つまり、最初の結果をマッピングして、オブザーバブルを返します。

observe(props, context) {
  if (!props.params.threadID) {
    return {};
  }

  const observeThread = ThreadStore.observeGetByID(
    {id: props.params.threadID}
  );
  return {
    thread: observeThread,
    messages: observeThread.map(thread => {
      return MessageStore.observeGetByIDs({ids: thread.messageIDs});
    })
  };
}

私は一般的にオブザーバブルにかなり慣れていないので、これについては完全に間違っているかもしれません。 約束の地では、これは非常に簡単です。 thenから約束を返すと、後続のthenがその約束に基づくようになるためです。

this.state依存してはいけないというコメントがわかりません。 カプセル化された状態は確かにReactをはるかに複雑にしますが、それだけです。 カプセル化された状態がない場合は、メモ化された即時モードライブラリのみが必要になります。 あなたが「ストア」にオールインするなら、そうです、あなたは州を必要としません、しかしそれはあなたがすることをReactが規定することではありません。

追加のラッパーを作成する必要があるパターンがいくつかありますが、通常の使用では作成する必要はありません。 ストア以外では、観測データは間接的であっても常に状態に依存します。 観察に頼るのは悪い習慣ではないと思います。 例えば

observe() {
  return { items: Items.getPagedItems({ pageIndex: this.state.currentPage }) };
}

observestateに依存していなくても、 propscontext依存します。 context依存していなくても、 contextを使用して、 observeそれを使用するコンポーネントの小道具をレンダリングする間接contextがあります。

小道具が変更された可能性があるため、レンダリングパスがダウンするたびにobserveを再評価する必要があります。 ただし、結果のオブザーバブルを確実に比較し、同じオブザーバブルが返された場合にサブスクライブ解除/再サブスクライブすることはありません。 ただし、(shouldComponentUpdateを使用する場合を除いて)個々のプロパティを区別することはできないため、パワー機能としてMapを使用して独自のキャッシュを実装するのが理想的です。 そうすれば、ツリー内の複数のコンポーネントに同じオブザーバブルを返すことができます。 たとえば、同じユーザーをロードする複数のコンポーネント。 そうしない場合でも、Observableを再作成し、最終的に最下位のキャッシュにヒットします。 これは、Reactの調整がとにかく機能する方法です。 それほど遅くはありません。

このobserveフックは、状態がObservablesにキャプチャされるという意味で、Reactを完全にリアクティブにするようには設計されていません。 現在のところ、重要な設計目標は、クロージャとコンビネータに状態がトラップされないようにすることであり、代わりに、凍結して復活させ、潜在的にワーカー間で共有できる、すっきりとした個別の状態ツリーを用意することです。

それが私の最後のポイントに私をもたらします...

これをコアライブラリに追加する必要はありません。 元の「パブリック」インターフェースはmountComponent / receiveComponentであり、その上に複合コンポーネントシステム全体を構築できました。 ただし、より高い抽象化バーによって可能になる他のものを構築できるようになったため、抽象化バーを上げる方がはるかに強力であると使用する人はあまりいませんでした。 コンポーネント全体の最適化など。

Reactの主な目的は、エコシステム内のさまざまな抽象化が共存できるように、コンポーネント間にコントラクトを作成することです。 その役割の重要な部分は、新しいクロスコンポーネント機能を有効にできるように、共通の概念の抽象化のレベルを上げることです。 たとえば、サブツリーのすべての状態を保存してから、サブツリーを復活させます。 または、サーバーでの自動アンマウントや、サーバーでの調整のタイミングの側面の変更を含めることもできます。

これらすべてを美味しく均一にするために、付属のバッテリーを用意することも重要です。

マイクロモジュール化(新しいライフサイクルフックの追加など)は、フレームワークに組み込むことに対する純粋な勝利ではないことを理解することが重要です。 また、システム全体の抽象化について推論することができなくなったことも意味します。

このようなものが「哲学/設計目標/非目標」としてドキュメントに含まれていることを望みます。

Reactの主な目的は、エコシステム内のさまざまな抽象化が共存できるように、コンポーネント間にコントラクトを作成することです。 その役割の重要な部分は、新しいクロスコンポーネント機能を有効にできるように、共通の概念の抽象化のレベルを上げることです。

これが大好き。 @gaearonに同意します。これが、ある種のドキュメントに含まれていればいいのですが。

確かにこれをコアライブラリに追加する必要はありません。しかし、より高い抽象化バーによって可能になる他のものを構築できるようになったため、抽象化バーを上げる方がはるかに強力であるという人はあまりいませんでした。 コンポーネント全体の最適化など。

(少なくとも私にとっては)控えめなのは、別のAPIを追加することではなく、非言語(まだ定義されている)構造に依存するAPIを追加することです。 これは完全にうまくいく可能性がありますが、Promiseライブラリが直面する問題について心配しています。Promiseライブラリ(仕様に準拠しているライブラリでさえ)が相互に信頼できないため、正しく解決するための不要なラッピングと防御作業が発生し、最適化の機会が制限されます。 。 さらに悪いことに、変更できない壊れた実装でjQueryのように立ち往生しています。

@jquense私は完全に同意します。 私はずっと前にこのフックを追加したかった。 (元の実験:https://github.com/reactjs/react-page/commit/082a049d2a13b14199a13394dfb1cb8362c0768a)

2年前の躊躇は、まだ標準化からかけ離れていることでした。 コアも追加する前に、標準プロトコルが必要でした。

多くのフレームワークがObservablesのようなものの必要性に同意し、標準化が口当たりの良いAPIが提案されるようになっていると思います。 最終的には少し調整する必要があると思いますが、高レベルのアーキテクチャが機能する限り、スワップ可能であり、最終的には収束するはずです。

Promisesで起こったことは、Observablesが苦しんでいない特定の領域でAPIとデバッグのストーリーが大幅に不足していたことだと思います。 これは、Promisesが最小限の不完全なソリューションを標準化する必要があった、すぐに使用できるより完全なストーリーです。

意見の唯一の違いは次のとおりです。私が観察した観測量(抵抗できませんでした、申し訳ありません)はZalgoの可能性です。 Observablesがサブスクリプションに応答して同期的に値をプッシュできるかどうか。 一部の人々はそれに反対しているように見えますが、ReactによるObservablesの使用は、私が理解している限り、これに依存します。 コメントしてもらえますか?

一般に、コンシューマーは常に制御されており、 observeOnようなものと常に非同期を選択する可能性があるため、ZalgoがObservablesの問題であるとは思いません。

これについて最終的にいくつかのコンセンサスが得られたことをうれしく思います。 私は個人的にObservablesよりもチャネルを好みますが、Observablesが言語に追加される場合は、これ以上待つ必要がないことに同意します。

そうは言っても、基本的なAPIに準拠する非Observablesで動作するようにAPIを十分に開いたままにしておくようにしましょう。

Zalgoも問題ではないと思います。 ただし、Observablesを使用すると、必要に応じてスケジューラーを使用して非同期を確保できます。デフォルトのスケジューラーは非同期になっているため、必要に応じて使用できます。

@sebmarkbageあなたは私の懸念のほとんどに対処したと思いますが、これをフレームワークに追加することの利点がわかりました。 ただし、 this.dataについてコメントできますか?(1)これらのフィールドをprops / context / stateに折りたたむことができますか、または(2)名前を変更できますか?

少しバイクシェッドですが、とにかくそれを目指しています... Zalgoの問題は、APIの期待の観点から重要であるかどうかではなく、観察可能な相互運用性と実装の容易さの1つです。 Promiseライブラリの世界を他のライブラリからのPromiseを処理するときに非常に防御的でなければならないという厄介な立場に置いた、Zalgoについての早期の合意がありません。 (私の上記の点は以下で繰り返されます)

...プロミスライブラリ(仕様の苦情ライブラリでさえ)がお互いを信頼できない場合、それはそれらが正しく解決されることを確認するための不必要なラッピングと防御作業につながります

初期の約束はすべて非同期解決に準拠していなかったため、現在、指定されたライブラリでさえthenablesが信頼に値すると想定できず、潜在的な最適化を殺す立場にあります。 これは、Reactが使用するObservable実装を提供しない場合(とにかく誰がそれを望んでいるのか)、ここで特に関連性があると私は思います。相互運用は重要です。 さらに、 @ gaearonのポイントに加えて、Reactが同期呼び出しに依存していて、常に非同期であることが指定されている場合、不正な実装で立ち往生しているというjqueryのような立場になります。

同意します。 私はずっと前にこのフックを追加したかった。 2年前の躊躇は、まだ標準化からかけ離れていることでした。 コアも追加する前に、標準プロトコルが必要でした。

参加して考えて良かったです、それは確かに慰めです。 :)そして一般的に、promiseの早期採用は、ここで説明している短所の価値があると思います。したがって、私の懸念を嫌い、または不承認と見なさないでください。このためのファーストクラスAPIの見通しにかなり興奮しています。また、ここではObservableが本当に良い/最も合理的な選択であることがわかります。

「ObservableのRxJSコントラクトを使用する必要があります。これは、より一般的に使用され、同期実行が可能になるためですが、 @ jhusainの提案がより一般的に使用されるようになったら、代わりにそのコントラクトに切り替えます。」

もう少しコンテキストを追加するだけです。 ノンブロッキングバックプレッシャを使用した非同期ストリーム処理の標準を提供するReactiveStreamsイニシアチブ(http://www.reactive-streams.org/)があります。 これには、ランタイム環境(JVMおよびJavaScript)とネットワークプロトコルを対象とした取り組みが含まれます。

現在の主要な実装は、fe AkkaStreamsまたはRxJavaです。 RxJがすでに同じインターフェース、つまりサブスクライバーの現在のインターフェースに準拠しているかどうかはわかりませんonSubscribe(Subscription s)、onNext(T t)、onCompleted()、onError(Throwable t)です。

@jhusainの提案にもっと光を当てることができますか?

Reactがこのイニシアチブに厳密に準拠する必要があるかどうかはわかりません。必要な場合は、おそらくRxJを間に置いて(準拠すると仮定して)Reactインターフェイスに適応し、RxJへのバックプレッシャーなどのより高度な概念を許可することができます(ただし、あまり適応する必要はありません)。

このイニシアチブに関して何か立場や目標はありますか?

@vladapこれは@jhusainからの言及された提案だと思います

@jhusainを読みましたが、将来この仕様に移行する動機についてはよく

Reactive-streams仕様はより大きなサポートがあり、すでにバージョン1.0になっています。 RxJavaはすでにこの仕様を実装しているので、RxJが続くと思います(ただし、チェックしていません)。

このブログでは、Akkaストリームを使用したいくつかの例でインターフェースを要約しています。

主に両方で作業しているため、バックエンドとフロントエンドで同じインターフェイスを使用することにはいくつかの利点があります。 バックエンドグループとフロントエンドグループの間で協力することはおそらく役立つかもしれませんが、一方で、websocketまたはsseがストリーミングの実際の統合ポイントであると思います。

現在、 www.reactive-streams.orgで実装者リストを見つけることができませんが、最後に確認したのは次のとおりです。

BjörnAntonsson– Typesafe Inc.
Gavin Bierman – Oracle Inc.
Jon Brisbin – Pivotal Software Inc.
ジョージキャンベル– Netflix、Inc
ベンクリステンセン– Netflix、Inc
マティアス・デーニッツ–spray.io
マリウスエリクセン– Twitter Inc.
Tim Fox – Red Hat Inc.
Viktor Klang – Typesafe Inc.
Roland Kuhn博士– Typesafe Inc.
ダグ・リー– SUNY Oswego
Stephane Maldini – Pivotal Software Inc.
Norman Maurer – Red Hat Inc.
Erik Meijer – Applied Duality Inc.
Todd Montgomery – Kaazing Corp.
Patrik Nordwall – Typesafe Inc.
ヨハンズ・ルドルフ–spray.io
Endre Varga – Typesafe Inc.

多分私はここに行き過ぎですが、より大きな文脈が将来の決定に役立つと信じています。

@vladap私が理解していることとgithubの問題で私が見ていることから、 @ jhusainはすでにそれらと連携しているので、それほど問題はないと思います。
インターフェイスの観点から、さまざまなgithubの問題やその他の仕様書で把握できることから、オブザーバーは確かにジェネレーターインターフェイスを尊重します。

{
  next(value),
  throw(e),
  return(v)
}

そのインターフェースを尊重する単一の「サブスクライブ」メソッドで非常に基本的なオブザーバブルを実装するだけで、反応に対して安全である必要があります。

同じ機能を持つ別の名前のように見えます。その意味では問題ありません。 私はおそらく仕様と同じ名前を好むでしょうが、結局、これらの方法が同じことをする限り、私はそれほど気にしません。

onSubscribe()に相当する欠落を評価することはできません。 私が言及したブログでは、著者はそれが背圧を制御するための鍵であると述べています。 他のユースケースがあるかどうかはわかりません。 このことから、Reactは背圧の制御を気にしないか、別の戦略があると思います。 それは複雑なことなので、Reactの問題ではないことを理解しています。

戦略が線に沿ったものであることを正しく理解していますか?アプリが複雑で背圧の問題が発生する可能性があるため、RxJSのようにその間に何かを使用して解決するか、Reactコンポーネントをfewebsocketに直接接続してくださいアプリはシンプルで更新が遅いため、バックプレッシャーの問題はありません。

将来のECMAScript用に提案された監視可能なインターフェイスはどこにありますか? すでにある場合。

現在の提案はここで見つけることができます:

https://github.com/jhusain/asyncgenerator

JH

2015年5月7日午前2時32分、 vladapnotifications @ github.comは次のように書いています。

将来のECMAScript用に提案された監視可能なインターフェイスはどこにありますか? すでにある場合。


このメールに直接返信するか、GitHubで表示してください。

Reactive Streams Proposal(RSP)は、背圧を処理するObservableを導入しているため、TC-39提案よりもさらに進んでいます。 RSP Observableは、バックプレッシャを尊重しながら、ネットワーク全体にストリームを効率的に送信するように最適化されています。 これは、非常に印象的なエンジニアリングであるRxJavaで行われた作業に一部基づいています(完全な開示:Netflixの同僚であるBen Christensenによって設計されました)。

より原始的なObservableタイプを標準化することを決定した主な理由は注意です。 より原始的なObservableは、ES2015 Iterableコントラクトのデュアルであり、このタイプは、ES2015ですでに標準化されているタイプと少なくとも同じくらい柔軟であるという貴重な保証を提供します。 さらに、JSには、背圧を必要としないObservablesのさまざまなユースケースがあります。 ブラウザでは、DOMはプッシュストリームの最も一般的なシンクであり、バッファのように効果的に機能します。 RSPタイプがより複雑であることを考えると、私たちのアプローチは、最初によりプリミティブなタイプを標準化し、次により高度なタイプを後で実装する余地を残すことです。 理想的には、ユーザーランドで検証されるまで待ちます。

FYI RxJSは現在、RSPObservableを実装する予定はありません。

JH

2015年5月7日には、2:30 AMで、vladap [email protected]書きました:

同じ機能を持つ別の名前のように見えます。その意味では問題ありません。 私はおそらく仕様と同じ名前を好むでしょうが、結局、これらの方法が同じことをする限り、私はそれほど気にしません。

onSubscribe()に相当する欠落を評価することはできません。 私が言及したブログでは、著者はそれが背圧を制御するための鍵であると述べています。 他のユースケースがあるかどうかはわかりません。 このことから、Reactは背圧の制御を気にしないか、別の戦略があると思います。 それは複雑なことなので、Reactの問題ではないことを理解しています。

戦略が線に沿ったものであることを正しく理解していますか?アプリが複雑で背圧の問題が発生する可能性があるため、RxJSのようにその間に何かを使用して解決するか、Reactコンポーネントをfewebsocketに直接接続してくださいアプリはシンプルで更新が遅いため、バックプレッシャーの問題はありません。


このメールに直接返信するか、GitHubで表示してください。

貴重な詳細に感謝します。 それは非常に理にかなっています。

@gaearon私はあなたをちょっとコピーしました。 ES6クラスでParseReactを使用したかったので、ミックスインのobserveapiを高次コンポーネントとして再実装する必要がありました。

https://gist.github.com/amccloud/d60aa92797b932f72649 (以下の使用法)

  • オブザーブをコンポーネントで定義するか、デコレータに渡すことを許可します。 (私は2つをマージするかもしれません)
  • オブザーバブルを小道具としてマージします(this.dataまたはthis.props.dataはありません)

@ aaronshaf @ gaearonファーストクラスにすることの利点は次のとおりです。

1)小道具の名前空間を食い尽くしません。 たとえば、高次コンポーネントは、他の目的には使用できないpropsオブジェクトからのデータのような名前を要求する必要はありません。 複数の高次コンポーネントをチェーンすると、より多くの名前が消費され続けるため、これらの名前を一意に保つ方法を見つける必要があります。 すでに作成されている可能性のあるものを作成していて、名前が競合している場合はどうなりますか?

また、高次コンポーネントのベストプラクティスは、ラップされたコンポーネントのコントラクトを変更しないようにすることだと思います。 つまり、概念的には、アウトと同じ小道具である必要があります。 そうしないと、消費者が受け取ったものとはまったく異なる小道具のセットを提供するときに、使用とデバッグが混乱します。

HOCの代わりに、通常のコンポーネントにすることができます。

import Observe from 'react/addons/Observe';

class Foo {
  render() {
    return (
      <Observe
        render={this.renderData}
        resources={{
          myContent: xhr(this.props.url)
        }} />
    );
  }

  renderData({ myContent }) {
    if (myContent === null) return <div>Loading...</div>;
    return <div>{myContent}</div>;
  }
}

render propとして渡される関数を制御するため、名前が衝突する方法はありません。 一方、それは所有者の状態を汚染しません。

ここで何が欠けていますか?

Observeコンポーネントがその小道具からObservablesを取得した場合、これはさらに冗長ではない可能性があります。

render() {
  return (
    <Observe myContent={Observable.fetch(this.props.url)}
             render={this.renderData} />
  );
}

renderData({ myContent }) {
  if (myContent === null) return <div>Loading...</div>;
  return <div>{myContent}</div>;
}

また、これの良い点は、 render入らないため、 shouldComponentUpdateがfalseを返した場合に、再サブスクリプションを回避できることです。

最後に、 Observeコンポーネントにラップするrenderデコレータを作成できます。

@observe(function (props, state, context) {
  myContent: Observable.fetch(props.url)
})
render({ myContent }) {
  if (myContent === null) return <div>Loading...</div>;
  return <div>{myContent}</div>;
}

データフェッチロジックを挿入するのではなく、純粋なレンダリング関数としてrenderを保持したいと思います。
私の意見では、最初の提案は良さそうです。 これは、状態がrx-reactでどのように機能するかに非常に近く、非常に一貫しているように見えるデータフェッチロジックから状態管理を分離することができます。

唯一気になるのは、1つのオブザーバブルではなく、オブザーバブルのマップを使用することです。これは、ユーザーがオブザーバブルの構成を選択する可能性がないためですが、これは小さな問題です。

データフェッチロジックを実際に挿入するのではなく、入力を節約するだけです。 <Observe />コンポーネントをレンダリングする上記のバージョンにデシュガーします。 ステートフルコンポーネントをレンダリングすることは珍しいことではないので、 renderが現在よりも純粋でなくなるとは思いません。

#3858のベストとこの提案を組み合わせてみました。

どのHOCアプローチでも、利点は明示性ですが、欠点は@sebmarkbageで説明されています。小道具の名前はある時点で競合する可能性があります。

現在の提案では、利点は明示性ですが、欠点はより複雑なライフサイクルとより大きなコアコンポーネントAPIサーフェスです。

#3858では、「メモ化されたレンダリング」の依存関係をレンダリング自体と同じ場所に配置することでメリットがあります(他の場所では使用されていないため、意味があります)が、 「同期しているように見えますが、非同期である」ことを懸念しており、どのように理解していないかを理解していません

私の提案では、コロケーションと明示性を維持していますが、次のようになります。

  • <Observe /> (またはレンダリングを<Observe />でラップするobserve()デコレータ)は単なるアドオンであり、Reactコアに_any_変更を提案するものではありません。
  • 誰もが自分のユースケースに合わせて独自の監視ロジックを実装できます。 必要に応じて、1つのオブザーバブルのみを使用する独自の<Observe />を作成できます。 デコレータを使用する場合と使用しない場合があります。
  • コンポーネントのライフサイクルは同じままです。
  • データはパラメータとして渡されるため、propの競合はありません。
  • 私たちは、私たちが持っているツールで問題を解決することによってドッグフーディングをしています。
  • ボイラープレートを削減するために、新しいコアコンセプトを導入する代わりに、ボイラープレート削減ツール(デコレータ)を使用します。

素晴らしい議論とこの辺りでの作業、多くの敬意。 :)

私はこれがそれをコアにすることに値しないことに同意します。 おそらく、アドオンはこの提案に十分な牽引力を与え、人々がより完全にコミットする前に収束して標準化しようとすることができます。 そうは言っても、この提案は、ミニマリズムの点でhttps://github.com/facebook/react/issues/3858https://github.com/facebook/react/pull/3920よりも優れていると思います。

これは私がサイドプロジェクトで使用しているものです(つまり、塩の粒)-これは

CoffeeScriptとミックスインに身を任せるか、必要に応じてES6のようになるまで目を細めてください。 :)

_ = require 'lodash'

module.exports = DeclareNeedsMixin = 
  componentDidMount: ->
    <strong i="12">@needsConsumerId</strong> = _.uniqueId @constructor.displayName
    <strong i="13">@sinkNeeds</strong> <strong i="14">@props</strong>, <strong i="15">@state</strong>

  componentWillUpdate: (nextProps, nextState) ->
    <strong i="16">@sinkNeeds</strong> nextProps, nextState

  componentWillUnmount: ->
    @props.flux.declareNeeds <strong i="17">@needsConsumerId</strong>, []

  sinkNeeds: (props, state) ->
    if not @declareNeeds?
      return console.warn 'Missing method required for DeclareNeedsMixin: `declareNeeds`', @

    needs = <strong i="18">@declareNeeds</strong> props, state
    props.flux.declareNeeds <strong i="19">@needsConsumerId</strong>, needs

  # Intended to be overridden by the host class.
  # Returns a set of facts, stored as an array.
  # Yes, immutable data is awesome, that's not the point here though. :)
  # Facts are serializable data, just values.
  # declareNeeds: (props, state) ->
  #   []

そしてこのように使用されます:

module.exports = EmailThreads = React.createClass
  displayName: 'EmailThreads'
  mixins: [DeclareNeedsMixin]

  propTypes:
    flux: PropTypes.flux.isRequired

  declareNeeds: (props, state) ->
    [Needs.GmailData.myThreads({ messages: 20 })]

  ...

したがって、 declareNeedsは、このコンポーネントに必要なものの説明に対する小道具と状態の一方向性関数です。 ここでの実際の実装では、トップレベルのコンポーネントに設定されている@props.flux.declareNeedsの受信側が、これらのニーズをProcessSinkオブジェクトに吸収します。 好きなようにバッチ処理し、同じfluxを共有するコンポーネント間でneeds重複を排除し、それらのニーズを満たすために副作用を実行します(ソケットへの接続やHTTPリクエストの作成など)。 参照カウントを使用して、ソケット接続などのステートフルなものを、それらを必要とするコンポーネントがなくなったときにクリーンアップします。

データは、ソケットイベントやリクエストなどのステートフルビットからディスパッチャに(そしてストアなどに)流れ、コンポーネントに戻ってニーズを満たします。 これは同期ではないため、データがまだ利用できない場合は、すべてのコンポーネントがレンダリングを処理します。

私はここで、これらの種類のソリューションをユーザースペースで探索することが可能であり、現在のAPIがその種の実験を非常にうまく提供しているという別の声としてのみこれを共有しています。 異なるアプローチ間の相互運用をサポートするためにコアが取ることができる最小限のステップに関して、 @ elierotenbergはそれを釘付けにしたと思います:

どちらかといえば、マウントされたReact階層の外でReactコンポーネントインスタンスのライフサイクルを維持することを公開してサポートすることは役に立ちます。

ステートレスアプローチに関しては、非同期データフェッチはステートフルであるように思われるため、一部のコンポーネントの状態で保留/完了/失敗のステータスを保存することが適切です。

@elierotenberg@andrewimmは、ファーストクラスのエラー処理について優れた点を示しています。 @sebmarkbage最小の相互運用点に対するあなたの本能は正しいと思いますが、コンポーネントツリーをバブルアップするためにエラーのセマンティクスを追加することがその要件にどのように適合するかはわかりません。 this.observedのスロットがいずれかのnext/error/completedの最後の値を保持している場合でも、 onErroronCompletedの値にアクセスする方法については、同様に単純なストーリーが必要です。 $ { next: "foo" } $のようなnext/error/completed #$。 観察可能な契約を一流の機能としてサポートすることなく、私はこの提案が削減されることに少し懐疑的です。

そして、これはインターネットであり、ここで初めてチャイムを鳴らしているので、Reactの問題のフィードは、優れた仕事やアイデアの最高の読み物であり、万能の情報源です。 :+1:

ビンディングのようなにおいがします。

これが現在の提案/実装とどのように関連しているかはわかりませんが、特にリアクティブデータソース(フラックス、フラックス)を使用する場合、合成と高次の操作を有効にすることが実際にデータ依存性追跡の重要な機能であることがわかりました有線、または更新を提供する以外のもの)。

react-nexus@^3.4.0次の例を見てください:

// the result from this query...
@component({
  users: ['remote://users', {}]
})
// is injected here...
@component(({ users }) =>
  users.mapEntries(([userId, user]) =>
    [`user:${userId}`, [`remote://users/${userId}/profile`, {}]]
  ).toObject()
))
class Users extends React.Component {
  // ... this component will receive all the users,
  // and their updates.
}

全体として、コンポーネントAPIにデータバインディングがあるべきかどうか疑問に思います。 高階コンポーネントを返すデコレータは、コンポーネントメソッドの名前空間を汚染することなくデータバインディングを表現する非常に優れた方法を提供しているように思われます。

ただし、 @ sebmarkbageが指摘したように、代わりにprops名前空間が汚染されるリスクがあります。 今のところ、私は小道具トランスフォーマーデコレーター( react-transform-props )を使用して、小道具を内部コンポーネントに渡す前にクリーンアップ/名前を変更していますが、高次のコンポーネントがより一般的であり、名前が衝突するリスクが高まります。
これは、プロパティキーである記号を使用して解決できますか? propTypesチェックはSymbolキー付きの小道具をサポートしていますか? JSXは計算されたインラインプロップキーをサポートしますか(ただし、計算されたプロパティ+オブジェクトスプレッドはいつでも使用できます)?

これが少し話題から外れてしまったら申し訳ありませんが、コンポーネントレベルでデータの深さを表現するための適切な抽象化/ APIを見つける必要があるようです。

私の2¢

時間の経過とともに変化する値の「関数としての子」パターンを楽しんでいます。 @elierotenbergが最初に思いついたと思いますか? 私の現在の使用法は、react-springsで(reboundを介して)springsをモデル化することです。 例 -

<Springs to={{x: 20, y: 30}} tension={30}>
  {val => <div style={{left: val.x, top: val.y}}>moving pictures</div>}
</Springs>

小道具の衝突はなく、所有者の状態の使用もありません。 複数のスプリングをネストすることもでき、reactがすべてのハードビットを管理します。 これらの「オブザーバブル」(ha!)は、 onErroronComplete 、およびその他の小道具(graphqlクエリ?)も受け入れることができます。

'flux'のためにこれをスケッチする大まかな試み

<Store 
  initial={0}
  reduce={(state, action) => action.type === 'click'? state+1 : state} 
  action={{/* assume this comes as a prop from a 'Dispatcher' up somewhere */}}> 
    {state => <div onClick={() => dispatch({type: 'click'})}> clicked {state} times</div>}
</Store>

Asanaではこのパターン(_render callbacks_と呼ばれる)を使用しましたが、最終的にはそれから離れつつあります。

長所

  • 小道具の衝突の可能性はありません。
  • StoreComponentの作成者とStoreComponentユーザーの両方として簡単に実装できます

短所

  • shouldComponentUpdateは、レンダリングコールバックが状態を閉じる可能性があるため、実装が非常に困難です。 A -> Store -> Bコンポーネントツリーがあると想像してください。 Aは、レンダリングコールバック中にB小道具としてアクセスされる状態のカウンターがあります。 カウンターが原因でA更新され、 Storeが更新されない場合、 Bには古いバージョンのカウンターがあります。 その結果、私たちは常にストアを更新することを余儀なくされました。
  • コンポーネントを個別にテストすることは非常に困難になりました。 必然的に、開発者は、レンダリングするコンポーネントのレンダリングコールバックに複雑なロジックを配置し、ロジックをテストしたいと考えていました。 これを行う自然な方法は、ツリー全体をレンダリングし、ストアコンポーネントをテストすることでした。 これにより、浅いレンダラーを使用できなくなりました。

StoreComponentReactElementを受け取り、ストアが新しいデータを受信すると、ストアは特定のプロップをオーバーライドするReactElementを複製するモデルに移行しています。 コンポーネントコンストラクターや小道具を使用するなど、このパターンを実行する方法はたくさんあります。 TypeScriptでモデル化するのが最も簡単だったので、このアプローチを採用しました。

素晴らしい点は、「横向き」の要件を破ることなく回避する方法を考えることはできません。

@threepointonehttps://github.com/reactjs/react-future/pull/28を強化するための実装提案の1つとまったく同じようにます

あなたの例の@ pspeter3では、ストアを常に更新することには重大な欠点がありますか/ shouldComponentUpdate: ()=> true ? とにかくチェーンにStoreがなければ、その「子」はレンダリングされたと思います。 御時間ありがとうございます!

@threepointoneそれはまさに私たちが境界のためにしたことです。 影響が正確に何であるかは不明でしたが、チームのメンバーからのパフォーマンスの懸念がありました。 テストの難しさと組み合わされた心配は、 React.cloneElement(this.props.child, {data: this.state.data})を使用するように切り替えを余儀なくされました

@ pspeter3テストの角度は間違いなく問題です。 浅いレンダラーが「レンダリングコールバック」を認識した場合はどうなりますか? それは役に立ちますか?

Ps- 'コールバックのレンダリング':thumbs_up:

@sebmarkbage es-observable [Symbol.observer]現在の議論subscribe 、同期/非同期APIの両方を持つことの有効性は現在議論されています。

私は同期サブスクリプションを支持して上記のユースケースでこのチケットにチャイムを入れていましたが、そこに追加するものがあるかどうかわかりませんでした。

ここでのアイデアは、外部状態を処理するための非常にクリーンなパターンだと思いますが、少し遊んだ後は、HOCアプローチに傾倒しています。

また、 @ gaearonは、静的なComposedComponent.observeを使用し、$# this.state.data this.stateを使用して、上記のHOCビットを簡略化しました-https ://gist.github.com/tgriesser/d5d80ade6f895c28e659

提案されたes7デコレータでは本当に見栄えがします。

<strong i="20">@observing</strong>
class Foo extends Component {
  static observe(props, context) {
    return {
      myContent: xhr(props.url)
    };
  }
  render() {
    var myContent = this.props.data.myContent;
    return <div>{myContent}</div>;
  }
}

クラスデコレータは、 dataのゲッターを追加して、元の提案されたAPIに非常に近づけることもできます(観察可能なサブスクリプションに影響を与えるローカル状態を差し引いたものは良いことです-周りのノイズがはるかに少ないサブスクライブ/サブスクライブ解除)。

同期サブスクリプションの欠如は大きな問題になる可能性があります。

「状態問題」と「SidewaysDataLoading」(派生データ)を一貫して解決する方法がわかったと思います。 ステートレスな「React-way」になります。 いつでも状態の一貫性を維持する方法を見つけました。これはパターンUI = React(state)に適合します。 完全に防弾にし、例を追加して、優れたプレゼンテーションを行うために手に負えないことはほとんどありません。 https://github.com/AlexeyFrolov/slt 。 一方、それは十分にテストされており、私はそれを本番プロジェクトで繰り返し使用しています。 賢い心が貢献することを歓迎します。

こんにちは、みんな、

私たちの会社では、半年前にこの提案で対処されたのと同じ問題に遭遇していたので、私が以前にこのスレッドでつまずいたことがなかったのはおかしいです。
大規模なプロジェクトでReactの使用を開始しました(非常に周期的なデータを使用する、Microsoft Visioの複雑さを備えたエディターを考えてみてください)。 バニラリアクションカウントは私たちのパフォーマンス要求に追いついていない、
そして、フラックスは、ボイラープレートが大量にあり、すべてのサブスクリプションでエラーが発生しやすいため、私たちにとっては少し無駄でした。 そのため、観察可能なデータ構造も必要であることがわかりました。

すぐに使用できるものが見つからなかったため、ノックアウトオブザーバブル(特に自動サブスクリプション)の原則に基づいて、独自のオブザーバブルライブラリを構築しました。
これは実際にはReactコンポーネントの現在のライフサイクルに非常にうまく適合しており、奇妙なハックや子レンダリングのコールバックさえ必要ありませんでした(以下で使用されるObserverMixinは約10 locです)。
これにより、DXが大幅に改善され、チームにとって非常にうまく機能したため、オープンソースとして公開することにしました。 その間、それは非常に戦闘で証明されており(たとえば、ES7の観測可能なアレイポリフィルを提供します)、高度に最適化されています。
これは短いタイマーの例です( JSFiddleとしても利用可能です)、私見ではDXはこれ以上良くなることはできませんでした...:安心:

var store = {};
// add observable properties to the store
mobservable.props(store, {
    timer: 0
});

// of course, this could be put flux-style in dispatchable actions, but this is just to demo Model -> View
function resetTimer() {
    store.timer = 0;
}

setInterval(function() {
    store.timer += 1;
}, 1000);

var TimerView = React.createClass({
    // This component is actually an observer of all store properties that are accessed during the last rendering
    // so there is no need to declare any data use, nor is there (seemingly) any state in this component
    // the combination of mobservable.props and ObserverMixin does all the magic for us.
    // UI updates are nowhere forced, but all views (un)subscribe to their data automatically
    mixins: [mobservable.ObserverMixin],

    render: function() {
        return (<span>Seconds passed: {this.props.store.timer}</span>);
    }
});

var TimerApp = React.createClass({
    render: function() {
        var now = new Date(); // just to demonstrate that TimerView updates independently of TimerApp
        return (<div>
            <div>Started rendering at: {now.toString()}</div>
            <TimerView {...this.props} />
            <br/><button onClick={resetTimer}>Reset timer</button>
        </div>);
    }
});

// pass in the store to the component tree (you could also access it directly through global vars, whatever suits your style)
React.render(<TimerApp store={store} />, document.body);

このアプローチの詳細については、このブログを参照してください。 ところで、ES6クラスを使用している場合は、デコレータやコンテナがライブラリに追加されることを確認します。

悲しいことに、react-europeの前にこのスレッドを見ていませんでした。そうでなければ、Reactとobservableについて話し合う良い機会があったでしょう。 しかし、刺激的な話に大いに感謝します! :+1:私は特にGraphQLの抽象化とReduxの背後にある思考作業が大好きでした:)

@mweststrateコミュニティは、最終的に「Observables」ソリューションと「Immutabledata」ソリューションのどちらかを選択する必要があると思います。 1つのソリューション(https://github.com/AlexeyFrolov/slt/issues/4)で両方のアプローチの長所を活用するには、何らかの方法で組み合わせる必要があるかもしれません。 私のソリューションでは、任意の時点での状態の一貫性に焦点を当てた「不変データ」アプローチを実装しました。 ObservablesとGeneratorsもサポートする予定です。 これは、「派生」または「補足」データ(ページ本文、アセット、推奨事項、コメント、いいねなど)をフェッチし、アプリの状態の一貫性を維持するために使用しているルールの例です。

https://github.com/AlexeyFrolov/slt#rules -example

これは、Locationヘッダーを使用してAPIリダイレクトを処理する方法を示す複雑な実世界(私の実稼働コード)の例です。

import r from "superagent-bluebird-promise";
import router from "./router";

export default {
    "request": function (req)  {
        let route = router.match(req.url);
        let session = req.session;
        route.url = req.url;
        return this
            .set("route", route)
            .set("session", req.session);
    },
    "route": {
        deps: ["request"],
        set: function (route, request) {
            let {name, params: { id }} = route;
            if (name === "login") {
                return this;
            }
            let url = router.url({name, params: {id}});
            let method = request.method ? request.method.toLowerCase() : "get";
            let req = r[method]("http://example.com/api/" + url);
            if (~["post", "put"].indexOf(method)) {
                req.send(request.body);
            }
            return req.then((resp) => {
                let ctx = this.ctx;
                let path = url.substr(1).replace("/", ".");
                if (!resp.body) {
                    let location = resp.headers.location;
                    if (location) {
                        ctx.set("request", {
                            method: "GET",
                            url: location.replace('/api', '')
                        });
                    }
                } else {
                    ctx.set(path, resp.body);
                }
                return ctx.commit();
            });
        }
    }
}

残りの部分では、Reactに直接バインドされていないことを除いて、あなたと同じアプローチです(私の場合は必要ありません)。 共通の目標をアーカイブするために、何らかの形で力を合わせる必要があると思います。

考えにくいエッジケースがたくさんあるので、それは悪い考えだと思います。

上記のコメントから、エンドユーザーが「データフル」コンポーネントを作成するたびに考える必要のあるスコープをリストできます。

  • サーバー側のレンダリング
  • .state.dataの初期化
  • .context.props.stateおよび.observeエンタングルメント
  • 非同期レンダリング

この提案は、エラーが発生しやすく、不安定で、コンポーネントのデバッグが困難になると思います。

@glenjamin connectおよびdisconnectライフサイクルフックによって提案されたものに同意します。

これがトピックから外れている場合はお詫びします(私にはよくわかりません)。 しかし、コンポーネントツリーと対話するためのAPIを公開することが、この問題に取り組むための興味深い方法であると私が考える理由の例を次に示します。https

この方法は、ツリーを歩くための私のハッカーです。したがって、私の質問は、この種のことを行うためのパブリックAPIを使用することで、「横向きのデータ読み込み」のイノベーションをどのように実現できるかということです。トップダウンで作業する」: https

しかし、コンポーネントツリーと対話するためのAPIを公開することが、この問題に取り組むための興味深い方法であると私が考える理由の例を次に示します。

@swannodetteがReactConfでこれについて話していたと思います。 オムネクストはそれをするのだろうか?

うん、彼は最初のReactConfで同じようなことを提案した。 私は最新のOm.nextを見たり、EuroClojureの話を聞いたりしていませんが、以前はOmは、これを行うためにユーザーが構築しなければならなかった独自の構造を使用していました。

この方法は、ツリーを歩くための私のハッカーです。したがって、私の質問は、この種のことを行うためのパブリックAPIを使用することで、「横向きのデータ読み込み」のイノベーションをどのように実現できるかということです。トップダウンで作業する」

これは、テストutilsで見られる浅いレンダリングとほぼ同等だと思います-ReactEuropeでの@sebmarkbageへのDOMおよび文字列レンダリングのピアとしてこれをファーストクラスのAPIにすることを述べました-0.14の変更はこれにうまく道を開くようです。

最初の反応は、それが少し低レベルかもしれないということです-しかし、拡張可能なWebのものと同様の方法で、これはユーザースペースでの実験をより簡単にするだろうと思います。

ツリーを取得するためにレンダリングする方法があれば、それを歩いて必要なすべてのデータを見つけて渡すことができることに同意します。

仮想DOMツリー全体にアクセスできることは、完全に別の問題として扱われている場合でも、アクセスしたい驚くほど強力で便利な機能です。
Reactが0.14以降で進んでいる道を考えると、それは非常に理にかなっていると思います。

オブザーバブルの複雑さを考えると、このスレッドの立派な紳士に、Meteorのリアクティブデータの実装を確認することをお勧めします: https://github.com/meteor/meteor/wiki/Tracker-Manual。 Reactとの調整には、 componentWillMountメソッドで文字通り3〜5行のコードが必要です。

  componentWillMount() {
    if (typeof this.getState === 'function') {
      Tracker.autorun(() => {
        // Assuming this.getState() calls some functions that return
        // reactive data sources
        this.setState(this.getState());
      });
    }
  }

Tracker(別のライブラリとして簡単に抽出できる)がReactでの観察可能なサポートの必要性を取り除くかどうかはわかりませんが、確かにそのように思われます。

MOBservableは非常によく似たパターンに従って、観測可能な値が変更されるとコンポーネントを横向きに更新します。したがって、現在のライフサイクルメソッドとデコレータは、サードパーティのライブラリがこれらの種類のパターンを表現し、サードデータソースの概念を理解するのに十分な柔軟性を提供しているようです。物事を複雑にしすぎるだけです。

    componentWillMount: function() {
        var baseRender = this.render;
        this.render = function() {
            if (this._watchDisposer)
                this._watchDisposer();
            var[rendering, disposer] = mobservableStatic.watch(() => baseRender.call(this), () => {
                    this.forceUpdate();
            });
            this._watchDisposer = disposer;
            return rendering;
        }
    },

@Mitranim同意します、それは本当に良い読み物です、それを見つけてくれてありがとう! それは事実上https://github.com/facebook/react/pull/3920が示唆していることです。

どちらの提案が良いかは未定です。 両方で遊んだことで、リアクティブプログラミング(リンクしたとおり、https://github.com/meteor/meteor/wiki/Tracker-Manual)が進むべき道であるとほとんど確信していますが、コンセンサスに達しておらず、私たちはまだ何が最も理にかなっているのかを理解しようとしているので、フィードバックを歓迎します。

@ Mitranim @ jimfb私はここ数年Meteorの大ファンです。 トラッカーは本当にクールでとても魅力的です。 非常に単純なバージョンのトラッカーがどのように機能するかを示すプレゼンテーションを作成しました。

https://github.com/ccorcos/meteor-track/blob/master/client/main.js

また、Trackerを使用して監視可能なストリームライブラリを作成しました。

https://github.com/ccorcos/meteor-tracker-streams

しかし、Blazeの代わりにReactを使用するように完全に移行したので、Trackerが複雑すぎて、単純なpublish / subsrcribe / onChangeメソッドの方が100倍簡単であることに気付きました。

また、すべての関数型プログラミングファンにとって、Trackerには、99%の確率で意味のあるいくつかの副作用がありますが、場合によってはそれが発生することもあります。 Reactコンポーネントでどのように表示されるかを次に示します

componentWillMount: ->
  <strong i="15">@c</strong> = Tracker.autorun =>
    @setState({loading: true})
    Meteor.subscribe 'users', => @setState({loading: false})
    Tracker.autorun =>
      @setState({users: Users.find({}, {sort:{name:-1}}).fetch()})
componentWillUnmount: ->
  @c.stop()

副作用についての私のポイントは、 c.stop()がサブスクリプションと内部自動実行も停止するということです。

@ccorcosはい、 https: //github.com/facebook/react/pull/3920では、すべての副作用は完全にReactの内部にあります。 実際、Reactコアは、ミューテーションを実行しない完全に不変/機能的な方法でそれらを実装できます。 ただし、副作用は外部からは見えないため、これは実装の詳細です。

興味深い...では、なぜonChangeコールバックを使用しないのですか? 私はそれらが最も互換性があることを発見しました。 Meteor(Tracker)、RxJS、Highland.jsなどを使用しているかどうかに関係なく、イベントコールバックを使用していつでも簡単に統合できます。 そして、Reactの高次コンポーネントについても同じです。

私がReduxで気に入っているのは、このロジックをフレームワークから除外し、Reactコンポーネントを純粋関数として保持することです。

@ccorcos主な問題は、コンポーネントインスタンスが破棄されたときに、すべての「サブスクリプション」をクリーンアップする必要があることです。 コンポーネントの作成者は、このクリーンアップを忘れることが多いため、メモリリークが発生します。 自動化することで、書き込みが容易になり(定型文が少なくなり)、エラーが発生しにくくなります(クリーンアップが自動化されます)。

@jimfbその通りです。 自動サブスクリプション解除は、Trackerのアプローチの非常に優れた点の1つです。 リアクティブデータソースを呼び出すたびにサブスクリプションが再確立され、コンポーネントがデータの呼び出しを停止すると、クリーンアップするものは何もありません。

ええ、でも実際には「自動的に」購読を解除しているわけではありません。 コンポーネントでc.stop()を呼び出す必要があります。マウントが解除されます。 ただし、ミックスインを使用してそれを抽象化することはできます。

componentWillMount: ->
  <strong i="7">@autorun</strong> =>
    @setState({loading: true})
    Meteor.subscribe 'users', => @setState({loading: false})
    Tracker.autorun =>
      @setState({users: Users.find({}, {sort:{name:-1}}).fetch()})

しかし、これは実際には他のAPIと何ら変わりはありません。 アンマウントなどで自動的にサブスクライブを解除する組み込みメソッドを作成できます。ほら、私はMeteorの大ファンです。 だから私はこれについてあなたに話したくありません。 たまに、なじみのない人にこのことを説明するのは本当に難しいと思います。 一方、単純なリスナー/イベントエミッターの使用は、理解と実装がはるかに簡単であり、使用したい反応性システムと非常に互換性がある傾向があります...

@ccorcosさまざまなメカニズムについて話している可能性があります。 最後に確認したところ、Trackerには永続的なサブスクリプションがありませんでした。 リアクティブデータソースへの依存関係を(それにアクセスすることによって)確立する各関数は、変更されると_1回_再実行され、それでサブスクリプションが終了します。 データソースに再度アクセスすると、もう1つの変更の「サブスクリプション」が再確立されます。 等々。

@Mitranimは正しく、#3920のセマンティクスはMeteorよりも「自動」であり(サブスクリプション解除は実際には自動です)、一般的なユースケースでは文字通りAPI表面積がゼロであるため、はるかに単純です。

@ccorcos @Mitranimすぐに使用できるTracker / Vue.jsに触発されたライブラリの場合、 Mobservableを試すことができます。これは、_render_中にアクセスされたすべてのデータを監視し、すべてのサブスクリプションをアンマウント時に破棄します(サブスクリプションが存続するまで)。 これまでのところ、Mendixのかなり大規模なプロジェクトにうまく適用できました。 独自のモデルオブジェクトを提供する代わりに、既存のオブジェクトを装飾するだけでなく、データの邪魔になりません。

最後に確認したところ、Trackerには永続的なサブスクリプションがありませんでした

@Mitranimサブスクリプションは永続的である可能性があります。 これをチェックしてください。

sub = Meteor.subscribe('chatrooms')
# this subscription lasts until...
sub.stop()

トラッカーには興味深いものがいくつかあります。 これをチェックしてください。

comp = Tracker.autorun ->
  Meteor.subscribe('chatrooms')
# this subscription lasts until...
comp.stop()

ただし、最後の例はそれほど有用ではありません。 このようなことをするまで。

roomId = new ReactiveVar(1)
comp = Tracker.autorun ->
  Meteor.subscribe('messages', roomId.get())
# when I change the roomId, the autorun will re-run
roomId.set(2)
# the subscription to room 1 was stopped and now the subscription to room 2 has started
# the subscription is stopped when I call stop...
comp.stop()

リアクティブデータソースへの依存関係を確立する(アクセスすることによって)各関数は、変更されたときに1回再実行され、それでサブスクリプションが終了します。

サブスクリプションは、自動実行が再実行されるか(リアクティブな依存関係が変更される)、またはそれが存在する計算が停止するまで続きます。 どちらもcalculation.onInvalidateフックを呼び出します。

これは、まったく同じことを実現した非常に工夫されたバージョンです。 うまくいけば、トラッカーがどのように機能するかを理解するのに役立つでしょう。 そして多分あなたはそれがどれほど厄介になるかを見るでしょう。

comp = Tracker.autorun ->
  Meteor.subscribe('chatrooms')

# is the same as

comp = Tracker.autorun (c) ->
  sub = null
  Tracker.nonreactive ->
    # dont let comp.stop() stop the subscription using Tracker.nonreactive
    sub = Meteor.subscribe('chatrooms')
  c.onInvalidate ->
    # stop the subscription when the computation is invalidated (re-run)
    sub.stop()
# invalidate and stop the computation
comp.stop()

@ccorcos混乱の原因がわかりました。 それは私の語彙でした。 サブスクリプションについて話すとき、私は_Meteorサブスクリプション_を意味しませんでした。 これらは、サーバーからクライアントにプッシュされるデータを決定しますが、この説明の主題であるビューレイヤーの更新には関係ありません。 _subscription_と言うとき、私は従来のイベントリスナーと、リアクティブデータソースに依存する関数を再実行するTrackerの機能との類似点を描いていました。 Reactコンポーネントの場合、それはデータをフェッチしてからsetStateまたはforceUpdateを呼び出すコンポーネントメソッドになります。

@mweststrate例をありがとう、それは面白そうだ。

そうそう。 だから彼らはそれを行う賢い方法を持っています。

componentWillMount: function() {
  this.comp = Tracker.autorun(() => {
    let sub = Meteor.subscribe('messages')
    return {
      loading: !sub.ready(),
      messages: Messages.find().fetch()
    }
  })
componentWillUnmount: function() {
  this.comp.stop()
}

サブスクリプションは、問題なく毎回再サブスクライブします。 sub.readyとMessages.find.fetchはどちらも「リアクティブ」であり、変更されるたびに自動実行をトリガーして再実行します。 Trackerの優れている点は、自動実行を非表示にし始め、特定の機能が「リアクティブコンテキスト」内にあることをドキュメントに記載している場合です。

これをミックスインに入れることができます

componentWillMount: function() {
  this.comp = Tracker.autorun(() => {
    return this.getReactiveData()
  })
componentWillUnmount: function() {
  this.comp.stop()
}

そして、あなたはちょうど機能するこの魔法のように反応する機能を残されています!

getReactiveData: function() {
  let sub = Meteor.subscribe('messages')
  return {
    loading: !sub.ready(),
    messages: Messages.find().fetch()
  }
}

トラッカーはこのようにかなりクールです...

@ccorcosトラッカースタイルのcomponentWillUnmountで停止する必要があります。そうしないと、データソースに到達し、 setStateを呼び出し続けます(現在非推奨になっているisMounted()を確認しない限り)。

ちなみに、コンポーネントメソッドをリアクティブにするためのエレガントなデコレータを作成できます。 次にいくつかの例を示します: [1][2] 。 このように見えます:

export class Chat extends React.Component {
  <strong i="13">@reactive</strong>
  updateState () {
    this.setState({
      auth: auth.read(),
      messages: messages.read()
    })
  }

  /* ... */
}

@Mitranimはかなりきちんとしています-私は高階関数を好みます。 ;)

@sebmarkbage @jimfb私はこのスレッドとaltスレッド(#3858)を数か月間フォローしてきましたが、コアチームがこの問題について、または少なくとも一般的な方向性についてコンセンサスに達したのかどうか知りたいです。

@oztune更新なし。 私たちは他の優先事項に焦点を合わせてきました。 このトピックに関する更新がある場合は、スレッドの1つに投稿します。

皆さん、私はコンテナを作成するためのユニバーサルAPIを作成しました。 react-komposerを確認してください。 それで、高階関数を持つコンテナを作成できます。

import { compose } from `react-komposer`;

// Create a component to display Time
const Time = ({time}) => (<div>{time}</div>);

// Create the composer function and tell how to fetch data
const composerFunction = (props, onData) => {
    const handler = setInterval(() => {
    const time = new Date().toString();
    onData(null, {time});
  }, 1000);

  const cleanup = () => clearInterval(handler);
  return cleanup;
};

// Compose the container
const Clock = compose(composerFunction)(Time);

// Render the container
ReactDOM.render(<Clock />, document.getElementById('react-root'));

ライブバージョンは次のとおりです: https

Promises、Rx.js Observables、Meteor'sTrackerを使用してコンテナーを作成する簡単な方法もいくつかあります。

これに関する私の記事もチェックしてください:いくつかのReactコンテナを作成しましょう

@arunoda私たちは非常に似たようなことをすることになりました。 私が疑問に思っていることの1つは、プロップを変更するたびにcomposerFunctionが呼び出されないようにする方法です。

@oztune実際に今再び実行されます。 このロッカとメテオを使用しています。 どちらもローカルキャッシュを備えており、composerFunctionを複数回呼び出してもサーバーにアクセスしません。

しかし、私たちはこのようなことをすることができると思います:

const options =  {propsToWatch: ["postId"]};
const Clock = compose(composerFunction, options)(Time);

何か案は?

@arunodaそれも私たちが試したことですが、アクションとその依存関係の間に少しの切断が生じます。 ここで、react-asyncに似た処理を実行します。ここでは、指定されたアクションをすぐに実行する代わりに、composerFunctionが関数とキーを返します。 キーがcomposerFunctionによって返された前のキーと異なる場合、新しい関数が実行されます。 これがこのgithubスレッドからの接線であるかどうかはわかりません。そのため、Twitter(同じユーザー名)で続行できれば幸いです。

@oztune新しいGHの問題を作成し、そこでチャットを続けましょう。 ツイッターよりずっといいと思います。

JSXにObservablesを直接理解してレンダリングさせて、Observableを小道具(this.props.todo $など)に渡してJSXに埋め込むことができるようにしないのはなぜですか。 その場合、APIは必要ありません。残りは、Reactの外部で管理され、HoCを使用してオブザーバブルを入力します。 小道具にプレーンデータが含まれているか、オブザーバブルが含まれているかは関係ありません。したがって、特別なthis.dataは必要ありません。

{this.props.todo $

さらに、Reactレンダリングは、追加のライブラリなしでリンクに記述されたデザインを可能にするOservable [JSX]をレンダリングできる可能性があります。

https://medium.com/@milankinen/containers -are-dead-long-live-observable-combinators-2cb0c1f06c96#.yxns1dqin

https://github.com/milankinen/react-combinators

こんにちは、
今のところ、rxjsストリームでステートレスコンポーネントを簡単に使用できます。
別のAPIの必要性を理解していません。
私は例を書きました-あなたがその上にマウスを動かすことができるボードとそれが26に達するとそれは再起動するように変わります。
このようにあなたがどう思うか聞いてみたいです。
コードは次のとおりです。
https://jsfiddle.net/a6ehwonv/74/

@giltig :私は最近この方法も学んでいて、気に入っています。 Cycle.jsと一緒に。

ブリッジングのためにサブジェクトを渡すことなく、コンポーネントで定義されたイベントハンドラーを簡単に聞くことができれば幸いです。 私が正しく理解していれば、ここで提案されていることとはほとんど逆です。 あるいは、React vdomの合成イベントを監視できる場合は、監視のために「refs」を使用して、監視するためだけにCSSタグを使用しないようにすることもできます。

さらにRxJはcombineLatestでオブジェクトをサポートできるため、React機能コンポーネントを直接使用してより大きなコンポーネントを作成できます。

const myFancyReactComponent = ({surface, number, gameover}) => (
        <div> 
          {gameover ? gameover : surface}
          {number}
        </div>
)

const LiveApp = Rx.Observable.combineLatest(
    LiveSurface, DynamicNumberView, DynamicGameOver,
    myFancyReactComponent
)

こんにちは、
Cycleやその他のフレームワークを使用しない理由は、フレームワークよりもライブラリを優先し(開発者の制御を強化)、Reactが持つコミュニティの力を楽しみたいからです。
現在、非常に多くのレンダリングエンジンがReact用に開発されており、それを使用しないのは残念です(ReactNative、ReactDom、ReactThreeなど)。 Rxjsだけを使用して、上記の例のように反応するのは非常に簡単です。

反応コンポーネントが、観察可能なものが小道具である限り、pojoを受け入れることができれば、考えが容易になります。 今のところそれは不可能なので、上に示したのは私たちが選んだ方法です。

ところで、MyFancyReactComponentで行ったことは可能であり、jsxを簡単に作成することもできますが、実際にそれを行った場合もあります。

主題に関して-最終的にReactコンポーネントでは、何でもかまいませんハンドラー関数を使用しているので、これは有効な方法だと思います。 私はイベントを受け取るサブジェクトを内部に実装することを選択しますが、他の誰かがそれ以外の方法を選択できます-その柔軟性。

反応コンポーネントが、観察可能なものが小道具である限り、pojoを受け入れることができれば、考えが容易になります。 今のところそれは不可能なので、上に示したのは私たちが選んだ方法です。

観察可能な小道具は、長期的には意味がありません。 実際、その文脈ではまったく意味がありません。

サスペンス(キャッシュ+コンテキスト)を使用した別のモデルになってしまったように思えます。 キャッシュ自体がサブスクリプションのサポートを取得する場合があります。 サスペンスの残りの作業はhttps://github.com/facebook/react/issues/13206で追跡でき

また、より孤立したケースのサブスクリプションパッケージも提供してい

このページは役に立ちましたか?
0 / 5 - 0 評価