コンテンツにスキップ

Vite + React + InversifyJS

InversifyJS をインストールする

1
$ npm i inversify reflect-metadata

tsconfig.jsonを設定する

以下をマージする。

tsconfig.app.json
1
2
3
4
5
6
7
{
  "compilerOptions": {
    "isolatedModules": false, // コンストラクタインジェクションする時に必要
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

vite.config.tsを設定する

Babel の場合

Note

コンストラクタインジェクションするときに動かない。

1
$ npm i -D @babel/plugin-proposal-decorators
vite.config.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    react({
      babel: {
        plugins: [["@babel/plugin-proposal-decorators", { legacy: true }]],
      },
    }),
  ],
});

SWC の場合

vite.config.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import react from "@vitejs/plugin-react-swc";
import { defineConfig } from "vite";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react({ tsDecorators: true })],
  optimizeDeps: {
    // 意味不明だがコンストラクタインジェクションする時に必要
    esbuildOptions: {
      tsconfigRaw: {
        compilerOptions: {
          experimentalDecorators: true,
        },
      },
    },
  },
});

コードを書く

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
.
├── README.md
├── eslint.config.js
├── index.html
├── package-lock.json
├── package.json
├── src
│   ├── App.tsx
│   ├── main.tsx
│   └── vite-env.d.ts
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
src/main.tsx
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import { Container, injectable } from "inversify";
import { createContext, StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "reflect-metadata";
import App from "./App.tsx";

export interface ISampleService {
  hi(): string;
}

@injectable()
export class SampleService implements ISampleService {
  hi(): string {
    return "Hi!";
  }
}

export const types = {
  SampleService: Symbol.for("SampleService"),
};

export const container = new Container();

container
  .bind<ISampleService>(types.SampleService)
  .to(SampleService)
  .inSingletonScope();

export const ContainerContext = createContext(container);

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <ContainerContext.Provider value={container}>
      <App />
    </ContainerContext.Provider>
  </StrictMode>
);
src/App.tsx
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import { useContext, useRef } from "react";
import { ContainerContext, ISampleService, types } from "./main";

function App() {
  const container = useContext(ContainerContext);
  const sampleServiceRef = useRef<ISampleService>(
    container.get<ISampleService>(types.SampleService)
  );
  return <p>{sampleServiceRef.current.hi()}</p>;
}

export default App;