gnaeus / react-ioc

Hierarchical Dependency Injection with new React 16 Context API
MIT License
201 stars 10 forks source link

Injection fails inside the constructor of React components #1

Open ghost opened 5 years ago

ghost commented 5 years ago

I wish I could give this project more stars, because it has saved me a lot of time and made my code much cleaner. Thanks for the awesome library!

There's one issue I'm having, though. I'm trying to use a dependency inside the constructor of a React component, and somehow the library always complains with the following error:

Error: Provider is not found.
  Please define Provider and set Loader.contextType = InjectorContext e.g.
  @provider([MyService, MyService])
  class App extends React.Component { /*...*/ }
  class Loader extends React.Component {
    static contextType = InjectorContext;
  }

I've set up everything in accordance with the documentation (I think).

@provider([Session, toClass(Session)])
class App extends React.Component {
  render() {
    if (this.state.loading) {
      return <Loader />
    }
    // ...
  }
}

class Loader extends React.Component {

  @inject session: Session

  constructor(props) {
    super(props)
    this.session.doSomethingUseful()  
  }

  render() {
    // ...
  }

  static contextType = InjectContext

}

Is there something I'm missing, or is this use case currently not supported by the library? Everything works fine if I defer accessing the dependency in <Loader /> to componentDidMount, so I'm guessing this is either a bug or a limitation.

gnaeus commented 5 years ago

Thanks! I should update the docs.

This library use React.Context under the hood. And if you want to use dependency inside constructor — you should pass the context argument to super(). See explainaition from Dan Abramov.

TL;DR If you don't pass props or context to super() React assign it to this.props and this.context immediately after your component constructor call. But if you want to use it inside constructor — you should call super(props, context).

@provider(Session)
 class App extends React.Component {
    render() {
      return <Loader />;
    }
}

class Loader extends React.Component {
  @inject session: Session;

  constructor(props, context) {
    super(props, context);
    this.session.doSomethingUseful();
  }
}

Also, if you use @inject decorator, you don't need to manually specify static contextType = InjectorContext. The decorator is already doing this for you.

ghost commented 5 years ago

Oooh I see, thanks for clarifying! Will try it out this evening to see if it works.

On 15 Jan 2019, at 09:34, Dmitry Panyushkin notifications@github.com wrote:

Thanks! I should update the docs.

This library use React.Context under the hood. And if you want to use dependency inside constructor — you should pass the context argument to super(). See explainaition https://overreacted.io/why-do-we-write-super-props/ from Dan Abramov.

TL;DR If you don't pass props or context to super() React assign it to this.props and this.context immediately after your component constructor call. But if you want to use it inside constructor — you should call super(props, context).

@provider(Session) class App extends React.Component { render() { return ; } }

class Loader extends React.Component { @inject session: Session;

constructor(props, context) { super(props, context); this.session.doSomethingUseful(); } } Also, if you use @inject decorator, you don't need to manually specify static contextType = InjectorContext. The decorator is already doing this for you.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/gnaeus/react-ioc/issues/1#issuecomment-454308120, or mute the thread https://github.com/notifications/unsubscribe-auth/ACpK647OSIABinpFu-WMIHk3Z2VlC7Cmks5vDZKugaJpZM4Z3rpi.