ReactコンポーネントのAPIコール実装

ブラウザアプリケーションは多くの場合、何らかのWebAPIクライアント機能を実装します。
API呼び出しの実装ポイントは、非同期処理の組込み方にあります。

Reactの関数コンポーネントでは、useEffect()内に実装できます。
APIコール部分は以下のような実装パターンになります。

const SomeComponent(props) => {

  useEffect(() => {
    (async () => {
      await call_api();
    })();
  }, []);
  
  return (
    <div>
    // SomeJSX Component
    </div>
  )
}

useEffectのコールバックにはasync関数を指定できません。
そのため、非同期の匿名関数ブロックを即時関数でラップするというややトリッキーな実装になります。

この例では分かりやすさのため非同期関数のコールにとどめていますが、実際にはステートの更新処理も実装することが多いでしょう。

コンポーネントの再描画はステートと連動するので、データ取得からステート更新までがuseEffectの主要機能になるでしょう。

非同期APIのテスト

react-testing-libraryをインストールすると、 Async Methodsを利用できます。

useEffect()の実装例のとおり、asyncブロック内の処理順序は守られても、非同期関数の完了を待つ直接的な手段はありません。

render()すると自動でAPIコールする挙動のコンポーネントでは、waitFor()で非同期処理の完了を待ちます。
待つ対象は実装ごとにケースバイケースですが、API結果に基いて描画されるDOMなどが適しています。

Jestの実装例は以下の通りです。

import React from 'react';
import { render, waitFor } from '@testing-library-react';

test("render API client", async () => {
  render(<SomeComponent />, doument.querySelector('#root'))
  await waitFor(expect(document.querySelector('#rendered')).not.toBeNull());
  
  // implement any assertion.
});

UI上の変化がないケースでは、ステートを更新する処理を追加し、更新後のステートをwaitFor()する実装が機能します。

非同期処理を伴うコンポーネントをテストする際、act()の実装を推奨するエラーが出ることがありますが、実際にはact()の実装が必要なケースはほぼ無いようです。

必要な対策は、テストコードに適切な完了待ちの処理を追加することです。

なお、テストコードではバグに対応する適切なエラーを得たいところですが、非同期処理関連のエラーがあった場合、適切なエラーを返せないことがあります。
その場合、何らかの問題があるということが唯一のヒントになります。

タイムアウトの延長設定

非同期処理の場合、Jestのタイムアウトの既定値内に処理が完了しないケースがあり得ます。
その場合、 jest.setTimeout()で設定を変更できます。

中馬崇尋
Chuma Takahiro