反応におけるサスペンス符号化の理解


TLR<Suspense> あなたの反応アプリでの非同期負荷を処理するためにクールで便利な方法ですが、それはPromise 私はオープンソースのこれらの落とし穴を避けるためにNPMパッケージを書いた-suspension .
私は、ファックスベースのバックエンドとの反応アプリを統合を実践するために少しのWebアプリで今週働いてきました.このプロジェクトの一環として、2009年に引っ張りましたreactfire , 私が使用した最初のモジュールは、最初のクラスのサポートを持っていた新しい反応<Suspense> コンポーネント.私は前にこのコンポーネントについて聞いただろうが、それは最終的には、それがどのように動作し、どのように私はより深く私の反応アプリ前方にそれを統合することができたどのように、深いダイビングを行う時間だった.

サスペンスとは
サスペンスは反応の最初の成分でしたexperimental Concurrent mode 非実験的なリリース( 16.6の方法)にマージされる.サスペンスの仕事は、async負荷の必要性を検出し、フォールバック読み込みUIをレンダリングすることです.
function CalendarApp() {
  const [viewedDay, setViewedDay] = useState(new Date());
  // Assuming that CalendarDayView is ready to work with Suspense,
  // this renders your loading spinner while today's data is loading.
  return (<main>
    <Suspense fallback={<LoadingSpinner />}>
      <CalendarDayView date={viewedDay} />
    </Suspense>
  </main>);
}
それがすべてであったならば、それは基本的にif (callStatus === "loading") { return <LoadingSpinner />; } . しかし、サスペンスは非常に少数の人々が話している超大国を持っていますが、理解するために、私たちは最初にこの構成要素がどのように機能するかを理解しなければなりません.

どのようにサスペンス動作しますか?
サスペンスは軽率に虐待するthrow 文.それがまだ荷を積んでいて、より多くの時間を必要とすることを示したい部品またはフックthrow エーPromise これは、コンポーネントが再描画の準備ができたときに解決されます.
function CalendarDayView({ date }) {
  // Let's imagine our ORM has a cache of days' agendas we can check
  const cacheResult = calendarDB.cachedValue({ date });

  // To hook into Suspense, we recognize if we need to load and
  // throw a Promise that resolves when we're ready to try again.
  if (!cacheResult) {
    const loadingPromise = calendarDB.load({ date });
    loadingPromise.then((result) => {
      calendarDB.cache({ date, value: result });
    });
    throw loadingPromise;
  }

  // Otherwise do the render
  return (
    <h1>Calendar for {cacheResult.dayString}</h1>
    // ... and so on
  );
}
我々throw このような約束は、反応を仮想DOMは、最寄りの<Suspense> コンポーネントと手の約束.
これは、レンダリングされたDOMからのサスペンスの下で木全体を削除し、fallback .
これはどのようにサスペンスは超大国を与えることです.だってthrow コンポーネントのレンダリングプロセスを中断します.上のカレンダの例では、ページの一番下にJSXに到達したらcacheResult がNULLで定義されていないため、負荷中に不足している値であることを防ぐ必要はありません.時Promise 我々が解決したか、拒絶するその<Suspense> 自動的に私たちのカレンダーを描画する別の機会を与えて、その子供を再レンダリングしようとします.

エラーの取り扱い
したがって、1つの小さなGotchaは、私たちが「ロード」ケースをうまく切り離したということです、しかし、我々の構成要素はまだ「API失敗」ケース自体に対処しなければなりません.さて、反応チームはあまりにもそのための提案を持ってthrow あなたError sとそれらをキャッチan error-boundary 木の上でより高い.あなたがサスペンスを使用することをコミットしているならば、これはあなたの構成要素をきちんとロードして、失敗して、成功するケースに分けるので、それはほとんど常に正しい答えです.これは特に簡単なおかげでreact-error-boundary パッケージ.

無限の負荷を避ける
このシステムでは大きなgotchaがあります:あなたはサスペンスが再び試みるときにどのように結果を持っていることを確認するのですか?サスペンスはその下に木を投げ捨てますので、約束を投げたコンポーネントの状態(および拡張子のフックの状態)は、ロード中に破壊されます.
これは、私たちの想像上のORMのようなAPIからロードされている場合は、ここでは簡単に値を得ることができる場合は、すでにキャッシュされているので、これは素晴らしいです.しかし、あなたが常に約束を返すAPIから何かをロードしているならばfetch , どのように、あなたは再試行されると言われるとき、結果を得ますか?場合は、単にうなり声が再び呼び出す場合は、すべてのリトライが別の呼び出しをキックオフ無限の負荷で立ち往生することができます.
この螺旋を逃れるために、あなたの外にあるキャッシュが必要です<Suspense> . これは、ファイアストアやアポロのような完全にキャッシュされたデータ層としての複雑なことができますまたはそれはあなたの外のstatotiveフックとして単純であることができます<Suspense> .

どのように、私は今日、私の約束でこれを使いますか?
そこで、以下のようにします.
  • <Suspense> コンポーネントのキャッチは、彼らがレンダリングする準備ができていない場合、子供たちがスローすると約束します.
  • 彼らは、彼らの子供をレンダリングから削除して、代わりにフォールバックを表示します.これは子供の状態を破壊する.
  • これのために、あなたはほとんどいつもデータのためにキャッシュを望みます.
  • このすべてをまとめると、既存の約束ベースのアクセスをサスペンスの準備に変換する最も簡単な方法は、コンポーネントが同期して結果を同期的に送ることができるトップレベルキャッシュを持つことです.あなたは既にアポロやReduxのような重いデータストア層を使用している場合は、それを使用することができます.あなたがそれらのうちの1つを使用していなかったならば、あなたはSTATHERYフックツールを使うことができました
    use-async-resource パッケージ.しかし、私は私がターゲットコンポーネントの内部で使用できるフックを欲しかったので、私は<Suspense> , それで私はそれを作りました.

    中断-どんな非同期APIもサスペンスするフック
    Suspension 上記のキャッシュとコールの設定を使用します.コンポーネントをラップします<SuspensionRig> また、サスペンスおよび/またはエラー境界の両方として作用することができるキャッシュプロバイダー.その後、約束からデータを必要とするたびに、フックを介してサスペンションに渡すと、ロードするか、スローするか、または値を返すかどうかを決定するロジックを処理します.
    ここではどのように我々は、サスペンションを使用する上記から我々のカレンダーアプリを書き換えるだろう.まず最初に、サスペンド用のベースサスペンスを交換します.
    import { SuspensionRig } from 'suspension';
    
    function CalendarApp() {
      const [viewedDay, setViewedDay] = useState<Date>(new Date());
      return (<main>
        <SuspensionRig fallback={<LoadingSpinner />}>
          <CalendarDayView date={viewedDay} />
        </SuspensionRig>
      </main>);
    }
    
    それから、我々は上から我々のキャッシュまたは荷ロジックを裂いて、それをuseSuspension フック
    import { useSuspension } from 'suspension';
    
    function CalendarDayView({ renderDay }: { renderDay: Date }) {
      // useSuspension takes a function that triggers your async work,
      //  a cache key to track the result, and the dependencies that
      //  trigger a new load (passed as args to your load function).
      const today = useSuspension(
        (date: Date) => calendarDB.load({ date }),
        'load-day-view',
        [renderDay]
      );
    
      // The hook guarantees that `today` will always be defined.
      return (
        <h1>Calendar for {today.dayString}</h1>
        // ... and so on
      );
    }
    
    キャッシングとトリガーの負荷と値のスローについてのすべてのロジックはフックとサスペンションに崩壊し、すべての私たちのために処理します.

    反応を待つ.futurede ()
    学習する<Suspense> この1週間は、反応についての私の興奮を再燃させました.我々のUIにおける同時負荷を理解するための,新しい簡素化メンタルモデルのような実験的並行特徴セットの全体が感じられる.
    サスペンションをチェックアウトしてください.npm install suspension 準備ができました.私はそれはあなたが<Suspense> 遅かれ早かれ自信を持って-あなたが役に立つか、問題に実行する場合は私に知らせてください.プロジェクトの課題とPRSは、要求と貢献のために開いています.
    View Suspension on Github 使い方の詳細を読むために.