InversifyJS icon indicating copy to clipboard operation
InversifyJS copied to clipboard

[React] inject is not defined

Open kegi opened this issue 4 years ago • 9 comments

I'm trying to use constructor injection with React. I'm using "customize-cra" and "react-app-rewire" to add babel plugins.

  • https://github.com/arackaf/customize-cra
  • https://github.com/timarney/react-app-rewired

I added "@babel/plugin-proposal-decorators" in legacy mode and "babel-plugin-parameter-decorator".

  • https://www.npmjs.com/package/@babel/plugin-proposal-decorators
  • https://www.npmjs.com/package/babel-plugin-parameter-decorator

config-override.js :

const { override, addDecoratorsLegacy, addBabelPlugins } = require('customize-cra')

module.exports = override(
    addDecoratorsLegacy(),
    addBabelPlugins(
        'babel-plugin-parameter-decorator',
    ),
)

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext",
      "es6"
    ],
    "typeRoots": [
      "node_modules/@types"
    ],
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "emitDecoratorMetadata": true,
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "forceConsistentCasingInFileNames": true,
    "isolatedModules": true,
    "jsx": "react",
    "module": "esnext",
    "moduleResolution": "node",
    "noEmit": true,
    "removeComments": true,
    "resolveJsonModule": true,
    "sourceMap": true,
    "skipLibCheck": true,
    "strict": true
  },
  "include": [
    "src"
  ]
}

Expected Behavior

Inversify should work in both DEV and PROD builds of React

Current Behavior

There is no error while building the production build but the app won't run and throw this on the browser console :

ReferenceError: inject is not defined

Note : This is working perfectly on DEV build.

Observations

  • the "inject" reference in the error is the Inversify annotation call (the one I'm using on my class constructor). I created a global function "inject" and was able to see all my dependencies sent as argument.
  • On the build, I see T=inject("myDependency")
  • I have the feeling that Inversify inject annotation is just not included on my production build.

Steps to Reproduce

import { injectable, inject } from 'inversify'

@injectable()
export default class MyClass {

    constructor(
        @inject('myDependency') private myDependency: MyDependencyInterface,
    ) {
    }
}

More information

  • I'm importing "reflect-metadata" on my very first line of code.
  • I'm only using constructor injection.
  • I'm using typescript and yarn
  • I'm not injecting dependencies on react components
  • I have a NodeJS project with almost the same configuration and I can use constructor injection without problem. I think that might be related to my tsconfig (which is slightly different)

Environment

OS : Windows 10 using WSL (Ubuntu 18.04) inversify : 5.0.1 reflect-metadata : 0.1.13 @babel/plugin-proposal-decorators : 7.8.3 babel-plugin-parameter-decorator : 1.0.15 react : 16.13.1 customize-cra : 0.9.1 react-app-rewired : 2.1.5 typeScript : 3.8.3 node : 13.12.0 yarn : 1.9.4

kegi avatar Apr 11 '20 16:04 kegi

Something similar is happening to me too, although can't get past an error. How did you at least make it work for development @kegi ?

cesalberca avatar Apr 13 '20 16:04 cesalberca

Something similar is happening to me too, although can't get past an error. How did you at least make it work for development @kegi ?

To make it work, I had to rewire react and add 2 babels plugins. I documented the steps here : https://github.com/inversify/InversifyJS/issues/1004#issuecomment-598776777

kegi avatar Apr 15 '20 10:04 kegi

I have same problem. Inversify work in dev mode, but not work in next build, only if inject using as constructor parameter decorator.

khomyakov42 avatar May 22 '20 04:05 khomyakov42

My solution for solve this problem .babelrc { "presets": [ [ "next/babel", { "class-properties": { "loose": true } } ] ], "plugins": [ "babel-plugin-transform-typescript-metadata", [ "@babel/plugin-proposal-decorators", { "legacy": true } ] ] } .tsconfig { "compilerOptions": { "baseUrl": ".", "module": "esnext", "esModuleInterop": true, "moduleResolution": "node", "target": "es2018", "downlevelIteration": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, "types": ["reflect-metadata"], "jsx": "preserve", "sourceMap": true, "skipLibCheck": true, "lib": [ "dom", "dom.iterable", "esnext" ], "allowJs": true, "strict": false, "forceConsistentCasingInFileNames": true, "noEmit": true, "resolveJsonModule": true, "isolatedModules": true, "paths": { "~/*": [ "src/*" ] } }, "include": [ "src/**/*" ], "exclude": [ "node_modules" ] }

khomyakov42 avatar May 22 '20 09:05 khomyakov42

it's happening for me as well, I have added two plugins in babel config

{
  "presets": [
    "@nrwl/web/babel",
    "@nrwl/react/babel"
  ],
  "plugins": [
    "babel-plugin-transform-typescript-metadata",
    "@babel/plugin-transform-block-scoping"
  ],
  "babelrcRoots": [
    "*"
  ]
}

now it's working but i don't know if it's the right solution

mohsinamjad avatar Jan 01 '21 12:01 mohsinamjad

still having this issue. what is the right solution here? do i need to eject CRA for solutions above?

da1z avatar Aug 02 '21 21:08 da1z

After using another library and facing the same issues, I went to the conclusion that on prod, since the code is uglified, Inversify have a hard time linking dependencies (my guess).

Usually, all other methods works (decorator, using a single object in the constructor holding the other dependencies, etc).

@da1z even if you eject, you will only be able to make it work on dev.

kegi avatar Aug 04 '21 18:08 kegi

i'm having same troubles in production and on dev things seems to work fine out of box

kishanio avatar Nov 25 '21 11:11 kishanio

solved with:


const createActivityStoreClass=(activityService: ActivityService)=>{

  @injectable()
  class ActivityStore{
    public all?:ActivityDto[];
    private activityService=activityService //from function argument
  
    constructor(){
      makeObservable(this,{
        all: observable,
        delete: action,
        load: action,
        create: action,
      })
      this.load();
    }
  
    load(){
      this.activityService.getAll().then((data)=>{
        this.all=data;
      })
    }
  
    async create(data:CreateActivity){
      return await this.activityService.create(data).then((res)=>{
        this.all?.unshift(res)
        return res;
      });
    }
  
  
    async delete(id:number){
      return await this.activityService.delete(id).then((res)=>{
        this.all = this.all?.filter((item) => item.id !== id);
        return res
      });
    }
  }

  return ActivityStore
}

export default class ActivityStore extends createActivityStoreClass(new ActivityService()){}

soekasir avatar Oct 10 '22 12:10 soekasir