鈴木颯介のブログ

技術的なメモとかポエムとか。

InversifyJSでMobXのStoreに注入します

Frozenエディタを開発するにあたって、localStorageとIndexedDBを扱う関数をMobXのStoreから叩いていたけど、テストが書きにくい。最近バイトでInversifyJSを使ってサーバーサイドの開発をやっているので、ここでも使ってみたい。

ServiceとそのMock、Interfaceを定義する

今回はとりあえずLocalStorageServiceを作りたい。その人がFrozenにアクセスしたのが初めてかどうかという情報をIS_FIRST_ACCESSというキーを使ってlocalStorageで管理ているので、そんな感じで書く。

// LocalStorageService.ts

import { injectable } from "inversify";

const IS_FIRST_ACCESS = "IS_FIRST_ACCESS";

export interface LocalStorageServiceInterface {
  setIsFirstAccess: () => void;
  getIsFirstAccess: () => string | null;
}

@injectable()
export class LocalStorageService implements LocalStorageServiceInterface {
  setIsFirstAccess(): void {
    localStorage.setItem(IS_FIRST_ACCESS, "false");
  }

  getIsFirstAccess(): string | null {
    return localStorage.getItem(IS_FIRST_ACCESS);
  }
}

function doNothing(): any {}

@injectable()
export class MockLocalStorageService implements LocalStorageServiceInterface {
  setIsFirstAccess = doNothing;
  getIsFirstAccess = doNothing;
}
// TYPES.ts
export default {
  LocalStorageService: Symbol.for("LocalStorageService")
};

ここは普通にInversifyJSを使うのと何も変わらない。本当はMockもちゃんと作ってあげるべきなのだけどここではとりあえずdoNothingで、、、。

Containerを作る

ここでinversify-inject-decoratorsという別のライブラリを使う。このライブラリを使うことで本来コンストラクタインジェクションができないような、別のライブラリ(ReactやMobXなの)が作るクラスのインスタンスに対してもコンストラクタインジェクションができるようになるらしい。

// container.ts

import "reflect-metadata";
import { Container } from "inversify";
import getDecorators from "inversify-inject-decorators";
import {
  LocalStorageServiceInterface,
  LocalStorageService,
  MockLocalStorageService
} from "./services/LocalStorageService";
import Types from "./services/Types";

const env = process.env.NODE_ENV;

const container = new Container();
export const { lazyInject } = getDecorators(container);
if (env != "development" && env != "production") {
  container
    .bind<LocalStorageServiceInterface>(Types.LocalStorageService)
    .to(MockLocalStorageService);
} else {
  container
    .bind<LocalStorageServiceInterface>(Types.LocalStorageService)
    .to(LocalStorageService);
}

こんな感じで環境に合わせてバインドするサービスを変えて、getDecoratorscontainerを食わせた結果のlazyInjectというやつをexportしてやる。

Storeで使う

InversifyJSの通常のinjectと同様にデコレータとして使って注入する。終わり。

// HogeStore.ts

import { lazyInject } from "../container"

export class HogeStore {
  @observable public hoge: Hoge = "hoge";
  @lazyInject(Types.LocalStorageService)
  private localStorageService!: LocalStorageServiceInterface;

  /* いろいろなメソッドら */
}

感想

inversify-inject-decoratorの挙動が全然わかっていないので雰囲気で使っていてよくないので、何をしてくれるライブラリなのかをしっかり把握しておきたい、、、。あとこの記事にあるコードはFrozenのソースから抜いてきて補完のない場所で改変して載せてるので、ミスっている場合があります。気づいたら教えてください:pray: