Can't get property dependency injection to work in React project

985 Views Asked by At

I need some really basic dependency injection for a react project in a similar syntactical manner as in C# or Java. Using context or injection through props is not an option. I tried three solutions so far:

However, none of theses solutions worked, which raised the questions if there is some sort of configuration issue.

Using Inversify / Inversify-React-Decorators / Rect.DI

SomeService.ts

import { inject, injectable } from "inversify";
import { TYPES, ITokenProvider, IFileService } from "../injectables";
import { lazyInject, DIContainer } from "../inversify.config";

export class SomeService {

    @inject(TYPES.ITokenProvider) private tokenProvider!: ITokenProvider;      //Inversify
    //@lazyInject(TYPES.ITokenProvider) private tokenProvider!: ITokenProvider;//Inversify-r-d
    //@Inject tokenProvider!: TokenProvider;                                   //react-di
     (...)
}

inversify.config.ts

import "reflect-metadata";
import { Container } from "inversify";
import getDecorators from "inversify-inject-decorators";

import { TYPES, ITokenProvider, } from "./injectables";
import { TokenProvider } from "./Services/TokenProvider"


const DIContainer = new Container();
DIContainer.bind<ITokenProvider>(TYPES.ITokenProvider).toConstructor(TokenProvider);


const { lazyInject } = getDecorators(DIContainer, false);

export { DIContainer, lazyInject }

injectables.ts

export interface ITokenProvider {
    getSomeToken(): Promise<string>
}

TokenProvider.ts

import "reflect-metadata";
import * as microsoftTeams from "@microsoft/teams-js";

import { injectable } from "inversify";
import { ITokenProvider } from '../injectables';

@injectable()
export class TokenProvider implements ITokenProvider {

    public constructor() { }

    public async getSomeToken(): Promise<string> {
        (...)
    }
}

App.tsx (used by react-di instead of inversify.config)

@Module({
    providers: [
        { provide: AccessTokenProvider, useClass: AccessTokenProvider },
    ]
})

Errors

React-DI and Inversify won'r resolve the dependency, causing an undefined error for the property. Inversify decorators causes following error:

TokenProvider.ts:8 Uncaught ReferenceError: Cannot access 'SomeService' before initialization
    at Module.SomeService (VM8 main.chunk.js:248)
    at Module../src/inversify.config.ts (inversify.config.ts:11)
   (...)

Config

tsconfig.json

{
  "include": [
    "src/*"
  ],
  "compilerOptions": {
    "target": "es5",
    "jsx": "react",
    "allowSyntheticDefaultImports": true,
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "types": [ "reflect-metadata" ]
  }
}

I also tried moving the "reflect-metadata" around different classes.

1

There are 1 best solutions below

1
On

Nested @lazyInject not working :https://github.com/inversify/InversifyJS/issues/941


The problem exists because of a circular dependency between a class (in the case above, the Header class) and the inversify.config'. inversify.config needs to import Headerin order to create the bindings but Header needs access to lazy Inject which is created ininversify.config.

The solution is to split your existing inversify.config into three files.

Does nothing except create the container (in my case it is called container.ts) Imports the container from the previous file and adds the bindings (in my case named inversify.config.ts) Imports file 1 (not 2!), calls getDecorators() and exports lazyInject Your class would then import lazyInject from that third file instead of inversify.config.