inversify / InversifyJS

A powerful and lightweight inversion of control container for JavaScript & Node.js apps powered by TypeScript.
http://inversify.io/
MIT License
11.29k stars 716 forks source link

Nested @lazyInject not working #941

Open inaiei opened 6 years ago

inaiei commented 6 years ago

I am writing a react app and I have a nested @lazyInject that is not working.

I am trying to use inversify to inject my components as well as other classes used by the components itself.

Here is an example of the problem I am having:

I have a header injected with @lazyInject in my App component.

export class App extends React.Component<{}, {}> implements IApp {
    @lazyInject(TYPES.Header)
    public header: React.ComponentClass<{}>;

    public render() {
        return (
            <div className="App">
                <this.header />
                <p className="App-intro">
                    To get started, edit <code>src/App.tsx</code> and save to reload.
                </p>
            </div>
        );
    }
}

then on the header component I have another lazyInject for the headerInfo object

export class Header extends React.Component<{}, {}> implements IHeader {
    @lazyInject(TYPES.HeaderInfo)
    public headerInfo: IHeaderInfo 

    public render() {
        return (
            <header className="App-header">
                <img src={logo} className="App-logo" alt="logo" />
                <h1 className="App-title">Welcome to React</h1>
            </header>
        );
    }
}

This is not working, but if I remove either one them and have just one lazyInject then it works fine.

Here is my inversify.config

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

import TYPES from './types';

import { App, IApp } from './components/App';
import { IHeader } from './components/IHeader';
import { IHeaderInfo, HeaderInfo } from './components/IHeaderInfo';
import { Header } from './components/Header';

const container = new Container();
const {lazyInject} = getDecorators(container);

const headerInfo = new HeaderInfo();
headerInfo.name = 'test';

container.bind<IApp>(TYPES.App).toConstructor<IApp>(App);
container.bind<IHeader>(TYPES.Header).toConstructor<IHeader>(Header);
container.bind<IHeaderInfo>(TYPES.HeaderInfo).toConstantValue(headerInfo);

export {
    lazyInject,
    container
};

Here is the error

TypeError: Object(...) is not a function
(anonymous function)
D:/Projects/PD/react/inversifyHello/src/components/Header.tsx:12
   9 | 
  10 | 
  11 | export class Header extends React.Component<{}, {}> implements IHeader {
> 12 |     @lazyInject(TYPES.HeaderInfo)
  13 |     public headerInfo: IHeaderInfo 
  14 | 
  15 |     public render() {
View compiled
./src/components/Header.tsx
D:/Projects/PD/react/inversifyHello/src/components/Header.tsx:23
  20 |             </header>
  21 |         );
  22 |     }
> 23 | }
  24 | 
  25 | export default Header;
  26 | 
View compiled
▶ 2 stack frames were collapsed.
./src/inversify.config.ts
D:/Projects/PD/react/inversifyHello/src/inversify.config.ts:1
> 1 | import 'reflect-metadata';
  2 | import { Container } from 'inversify';
  3 | import getDecorators from 'inversify-inject-decorators';
  4 | 
View compiled
▶ 5 stack frames were collapsed.
./src/index.tsx
D:/Projects/PD/react/inversifyHello/src/index.tsx:1
> 1 | import * as React from 'react';
  2 | import * as ReactDOM from 'react-dom';
  3 | import App from './components/App';
  4 | import './index.css';
betko commented 5 years ago

Hi @inaiei,

Did you find any solution for this problem?

xxyuze commented 5 years ago

I have the same problem in vue.js Did you find any solution for this problem?

spali commented 5 years ago

I had a similar problem... just in case it helps someone. In my case I had exported the container and decorator from a ioc.ts file which was itself exported via an index.ts file. That worked for basic DI, but didn't work for the lazyInject decorator as soon as used in two "circular" classes. the import of all exports of the ioc stuff resulted in undefined. As soon as I changed to directly import from the ioc.ts instead of the index.ts it started working. So instead of use this two files, I just put everything from ioc.ts directly in the corresponding index.ts and it still worked.

did not work, as soon consumed by more than one file that depend on each other.

src
  ioc
    ioc.ts    <-exports the container and decorators (but never consumed directly by any other file)
    index.ts  <-export * from ioc.ts
  class1.ts     <- import via src/ioc
  class2.ts     <- import via src/ioc

when class1.ts and class2.ts depends on each other, the following import paths do not work: class1.ts -> src/ioc/index.ts -> src/ioc/ioc.ts class2.ts -> src/ioc/index.ts -> src/ioc/ioc.ts

works:

src
  ioc
    index.ts    <-exports the container and decorators
  class1.ts     <- import via src/ioc/ioc.ts
  class2.ts     <- import via src/ioc/ioc.ts

when class1.ts and class2.ts depends on each other, the following import paths do work: class1.ts -> src/ioc/index.ts class2.ts -> src/ioc/index.ts

PS: the strange thing is... I used breakpoints to check if class1.ts or class2.ts was called before ioc.ts but this was not the case... so something behaves badly with the module resolution.

jordanlytle commented 4 years ago

I realize this is an old issue, but I ran into this problem recently and found a workaround.

The problem exists because of a circular dependency between a class (in the case above, the Header class) and the inversify.config'.inversify.configneeds to importHeaderin order to create the bindings butHeaderneeds access tolazyInjectwhich is created ininversify.config`.

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

  1. Does nothing except create the container (in my case it is called container.ts)
  2. Imports the container from the previous file and adds the bindings (in my case named inversify.config.ts)
  3. Imports file 1 (not 2!), calls getDecorators() and exports lazyInject

Your class would then import lazyInject from that third file instead of inversify.config.

Pines-Cheng commented 2 years ago

I have the same problem in react.