Chapter 04

1章.3 よりよいメンタルモデル形成:解決する課題の抽出

Satoshi Takeda
Satoshi Takeda
2021.09.30に更新

どういった課題を解決しているかを考える

開発者の出自や文化形成によってコードや構成などの表層を見て、React・Angular に認知的親和性があるかどうかは人によりけりですが、解決している課題が同じという場合もあると筆者は考えます。

Angular は Constructor Injection によって依存オブジェクトを解決していました。これによりユニットテストはインタフェースを模したオブジェクトを注入することで本物を使うことなくコンポーネントのテストケースに集中できます。下記の場合は HttpClient を注入しています。

@Component({/* metadata... */})
class AviewComponent {
  constructor(
    /**
     * 以下のインタフェースをモックすることでユニットテストが可能になる
     */
    private http: HttpClient
  ) {}
}

これは実体である HttpClient モジュールとの密結合を避け疎な状態を保ちながら、AviewComponent の変更容易性やテスタブルな状態を保つことができるといったメリットがあります。

React は DI が存在しないのか

React は上記のような依存の注入ができないのでしょうか。以下のコードを見てみましょう。

const HttpClientContext = createContext();
const cliet = new HttpClient({/* config... */});

function HttpClientProvider({ children }) {
  return (
    <HttpClientContext.Provider value={client}>
      { children }
    </HttpClientContext.Provider>
  );
}

function App() {
  return <HttpClientProvider><Foo /></HttpClientProvider>
}

function Foo() {
  const client = useContext(HttpClientContext);
  useEffect(() => {
    client.get('https://foo/api/user').then((data) => {
      // setState(data);
    });
  }, []);
  return (<>{/* ... */}</>);
}

最適かどうかはさておき、Context を使ったひとつの実装例です。ContextProvider によって渡される HttpClient がコンポーネントをまたいでも利用できるよう、依存(オブジェクト)が注入されているということがわかります。

useContext が密結合かどうかについては議論はありそうですが、Foo コンポーネントをテストする場合は Provider を別途作成し HttpClient を模したオブジェクトを注入することでテストが可能になるでしょう。テストの方針にもよりますが、通信自体をモックする方法もありますので参考ください

test('Foo render', async () => {
  const client = {/* HttClient のインタフェースをモックしたオブジェクトなど */}
  render(
    <HttpClientContext.Provider value={mockClient}>
      <Foo />
    </HttpClientContext.Provider>
  );
});

GraphQL クライアント・React 向けのライブラリを提供する Apollo でも MockedProvider を提供しています。コンポーネントを構成する際には提供されている適切なテストスイートがないか、もしくはテスタブルにしておくことができないか事前に確認しておいもよいかもしれません。

普段意識することはほとんどありませんが、ここで扱った React と Angular における依存オブジェクトの注入は何を解決するものだったかを意識しておくことでフィールドが変わっても取り回しの聞く知識になることは間違いないでしょう。