2

MobX を使用しているときにプロパティを遅延読み込みする現在の慣用的な方法は何ですか?

私はこれに数日間苦労してきましたが、厳密モードが問題になって以来、良い例は見つかりませんでした。厳密モードのアイデアは気に入っていますが、遅延読み込みはそれと矛盾していると思い始めています (プロパティにアクセスまたは監視すると、データがまだ存在しない場合、データを読み込むという副作用が発生するはずです)。

それが私の質問の核心ですが、私がどのようにしてここにたどり着いたかを確認するには、読み続けてください.

私の現在のセットアップの基本(大量のコードを投稿せずに):

React コンポーネント 1 (ListView): componentWillMount

  1. componentWillMount & componentWillReceiveProps - コンポーネントは、ルート パラメーター (react-router) からフィルター値を取得し、ListView で監視可能なオブジェクトとして保存し、それに基づいて「提案」をフェッチするようにストアに指示します。
  2. Store.fetchProposals は、そのリクエストがすでに作成されているかどうかを確認します (リクエストは ObservableMap に格納され、フィルター オブジェクトをシリアル化することによってキーが保存されるため、2 つの同一のフィルターが同じ応答オブジェクトを返します)。必要に応じてリクエストを行い、リクエストが終了したかエラーがあるかに関する情報を含む監視可能なレスポンス オブジェクトを返します。
    1. ListView は、監視可能な応答オブジェクトをプロパティとして保存するため、読み込みまたはエラー インジケーターを表示できます。
    2. ListView には、フェッチに使用されるのと同じフィルター オブジェクトを使用して Store.getProposals を呼び出す計算済みプロパティがあります。
    3. Store.getProposals は、フィルター オブジェクトを受け取り、ObservableMap (proposal.id のキー) からすべての提案を取得し、フィルター オブジェクトを使用してリストをフィルター処理し、Proposal[] を返すトランスフォーマーです (フィルターに一致するものがない場合を含め、フィルターに一致するものがない場合は空です)。プロポーザルはまだ読み込まれています)

これはすべてうまくいくようです。

問題は、プロポーザルに client と clientId のプロパティがあることです。Proposal.clientId は、提案と共に読み込まれる文字列です。クライアントが実際にアクセスされて、ストアにサーバーからフェッチするように指示するまで待ちたいです(まだストアにない場合)。この場合、ListView はたまたまクライアント名を表示するため、プロポーザルの直後にロードする必要があります。

私が得た最も近いものは、提案のコンストラクターリストに自動実行を設定することですが、その一部は、私が意図している場所に反応していません。(関連するセクションに切り捨てられます):

@observable private clientId: string = '';
@observable private clientFilter: IClientFilter = null;
@observable client: Client = null;

constructor(sourceJson?: any) {
    super(sourceJson);
    if (sourceJson) {
        this.mapFromJson(sourceJson);
    }
    //this one works. I'm turning the clientId string into an object for the getClients transformer 
    autorun(() => { runInAction(() => { this.clientFilter = { id: this.clientId }; }) });
    autorun(() => {
        runInAction(() => {
            if (this.clientId && this.clientFilter) {
                const clients = DataStore.getClients(this.clientFilter);
                const response = DataStore.fetchClients(this.clientFilter);
                if (response.finishedTime !== null && !response.hasErrors) {
                    this.client = clients[0] || null;
                    console.log('This is never called, but I should see a client here: %o', DataStore.getClients(this.clientFilter));
                }
            }
        })
    });
}

応答オブジェクトは観察可能です:

export class QueryRequest<T extends PersistentItem | Enum> {
    @observable startTime: Date = new Date();
    @observable finishedTime: Date = null;
    @observable errors: (string | Error)[] = [];
    @observable items: T[] = [];
    @computed get hasErrors() { return this.errors.length > 0; }
    @observable usedCache: boolean = false;
}

システムと戦っているような気がしますが、コンストラクターで自動実行を設定するのは理想的ではないようです。このパターンを合理的な方法で解決できる人はいますか? 私のセットアップがおかしく見える場合は、全体的な提案を受け付けています。


編集 1: わかりやすくするために @Mobx を削除しました。


編集 2: 状況を再評価しようとして、(再び) 優れた lib mobx-utilsを見つけました。これには、私のニーズに合う可能性のあるlazyObservable関数があります。現在、次のようになっています。

client = lazyObservable((sink) => {
    autorun('lazy fetching client', () => {
        if (this.clientFilter && this.clientFilter.id) {
            const request = DataStore.fetchClients(this.clientFilter);
            if (request.finishedTime !== null && !request.hasErrors) {
                sink(request.items[0]);
            }
        }
    })
}, null);

これは機能しています!

このオブジェクトのclientId/clientFilterプロパティに基づいて更新するには、そこに自動実行が必要だと思います(このオブジェクトが後で新しいクライアントに割り当てられた場合、lazyObservableを更新したいと思います)。怠惰なプロパティの定型文は気にしませんが、そこでの提案には間違いなくオープンです。

これが最終的に進むべき道である場合、監視可能なリクエストオブジェクトの代わりに、同じライブラリからfromPromiseも調べます。古さをチェックするために開始時間を追跡しているため、わかりません。他の誰かがそれに出くわしていない場合に備えて、ここにリンクします:)

4

1 に答える 1

2

私は自分のプロジェクトで別のアプローチを使用しており、それを別の npm パッケージに抽出しました: https://github.com/mdebbar/mobx-cache

簡単な例を次に示します。

まず、クライアント情報を表示するための React コンポーネントが必要です。

@observer
class ClientView extends React.Component {
  render() {
    const entry = clientCache.get(this.props.clientId)

    if (entry.status !== 'success') {
      // Return some kind of loading indicator here.
      return <div>Still loading client...</div>
    }

    const clientInfo = entry.value
    // Now you can render your UI based on clientInfo.
    return (
      <div>
        <h2>{clientInfo.name}</h2>
      </div>
    )
  }
}

次に、以下をセットアップする必要がありますclientCache

import MobxCache from "mobx-cache";

function fetchClient(id) {
  // Use any fetching mechanism you like. Just make sure to return a promise.
}

const clientCache = new MobxCache(fetchClient)

それはあなたがする必要があるすべてです。MobxCache は必要なときに自動的に呼び出しfetchClient(id)、データをキャッシュします。

于 2016-09-11T00:00:44.677 に答える