react-ioc
TypeScript icon, indicating that this package has built-in type declarations

1.0.0 • Public • Published

React IoC

Hierarchical Dependency Injection for React

Build Status Coverage Status GitHub license npm version

Features

  • Hierarchical Dependency Injection
  • Can inject dependencies using React Hooks
  • Automatically calls .dispose() on created class instances when Recat unmouts Provider component
  • Can work without decorators
  • Supports lazy service registration with code splitting
  • ES6, CommonJS and UMD bundles
  • Declarations for TypeScript and Flow
  • Type Safe even in JavaScript (with TypeScript --checkJs mode)
  • Tiny: only 1.1 KB (min+gzip)

Requirements: React 16.6 or greater, ES6 Map or Map polyfill.

Documentation

arch

Example

import React from "react";
import { provider, inject } from "react-ioc"
import { obserbvable, action } from "mobx";
import { observer } from "mobx-react";
 
class DataContext {
  users = observable.map<number, User>();
  posts = observable.map<number, Post>();
}
 
class PostService {
  @inject dataContext: DataContext;
 
  @action
  createPost(user: User) {
    const post = new Post({ id: uniqueId() });
    this.dataContext.posts.set(post.id, post);
    return post;
  }
}
 
@observer
class PostEditor extends React.Component {
  @inject postService: PostService;
 
  render() {
    // ...
  }
}
 
@provider(DataContext, PostService)
class App extends React.Component {
  render() {
    // ...
  }
}

@provider (alias @Provider)

HOC (or decorator) that registers dependencies in scope of wrapped component.

import { provider, toClass, toFactory, toValue, toExisting } from "react-ioc";
 
@provider(
  DataContext,                          // bind DataContext to self
  [IFooService, FooService]             // bind IFooService to FooService
  [IBarService, toClass(BarService)]    // bind IBarService to BarService
  [IBazService, toValue({ baz: 123 })]  // bind IBazService to static value
 
  // bind MobxStore to factory with dependencies
  [MobxStore, toFactory(
    [IFooService, IBarService],
    (fooService, barService) => MobxStore.create(fooService, barService)
  )]
 
  // bind IObsoleteService to already registered IFooService instance
  [IObsoleteService, toExisting(IFooService)],  
)
class App extends React.Component {
  render() {
    // ...
  }
}

Providers can be nested:

@provider(DataContext, AuthService)
class App extends React.Component {
  render() {
    // ...
  }
}
 
@provider(UserService)
class HomePage extends React.Component {
  render() {
    // ...
  }
}

Also Provider component has static register() function, for imperative dependencies registration:

// App.jsx
import { provider, toClass } from "react-ioc";
 
class App extends React.Component {}
 
export default provider()(App);
// somewhere else
import App from "./App";
 
App.register(FooService, [BarService, toClass(BarService)]);

▲ back to top ▲

@registerIn (alias @RegisterIn)

Class decorator for lazy service registration in Provider. Accepts lambda that returns some Proveider component.

// ./services/LazyService.js
import { registerIn } from "react-ioc";
import App from "../components/App";
 
@registerIn(() => App)
export class LazyService {}
// ./components/LazyWidget.jsx
import { inject } from "react-ioc";
import { LazyService } from "../services/LazyService";
 
export default class LazyWidget extends React.Component {
  @inject lazyService: LazyService;
}
// ./components/App.jsx
import { provider } from "react-ioc";
const LazyWidget = React.lazy(() => import("./LazyWidget"));
 
@provider()
export default class App extends React.Component {
  render() {
    return (
      <React.Suspense fallback={<div>Loading...</div>}>
        <LazyWidget />
      </React.Suspense>
    );
  }
}

Also, is can accept binding as second argument:

// ./services/LazyService.js
import { registerIn, toClass } from "react-ioc";
import App from "../components/App";
 
interface LazyService {
  method(): void;
}
 
class LazyServiceImpl implements LazyService {
  // ...
}
 
@registerIn(() => App, toClass(LazyServiceImpl))
export class LazyService {}

▲ back to top ▲

@inject (alias @Inject)

Property decorator for property dependency injection.

Can use dependency types from Reflect Metadata (with TypeScript --emitDecoratorMetadata):

import { inject } from "react-ioc";
 
class FooService {
  @inject barService: BarSerivce;
}
 
class MyComponent extends React.Component {
  @inject fooService: FooService;
  @inject barService: BarSerivce;
  // ...
}

Or manually specified dependencies:

import { inject } from "react-ioc";
 
class FooService {
  @inject(BarSerivce) barService;
}
 
class MyComponent extends React.Component {
  @inject(FooService) fooService;
  @inject(BarSerivce) barService;
  // ...
}

▲ back to top ▲

inject

Utility function for property or constructor dependency injection. Note, that for React Components we should explicitely define static contextType = InjectorContext (unlike with @inject decorator).

Property Injection:

import { inject, InjectorContext } from "react-ioc";
 
class FooService {
  barService = inject(this, BarSerivce);
}
 
class MyComponent extends React.Component {
  fooService = inject(this, FooService);
  barService = inject(this, BarSerivce);
  
  static contextType = InjectorContext;
}

Constructor Injection:

import { inject } from "react-ioc";
 
class OtherService {
  constructor(fooService, barService) {
    this.fooService = fooService || inject(this, FooService);
    this.barService = barService || inject(this, BarSerivce);
  }
}

▲ back to top ▲

useInstance React Hook

import { useInstance, useInstances } from "react-ioc";
 
const MyButton = props => {
  const myService = useInstance(MyService);
 
  return <button onClick={() => myService.doSomething()}>Ok</button>
}
 
const MyWidget = props => {
  const [fooService, barService] = useInstances(FooService, BarService);
 
  return <div><MyButton /></div>
}

▲ back to top ▲

Usage

> npm install --save react-ioc

UMD build

<script crossorigin src="https://unpkg.com/react-ioc/dist/index.umd.min.js"></script>
const { provider, inject } = window.ReactIoC;

Package Sidebar

Install

npm i react-ioc

Weekly Downloads

1,646

Version

1.0.0

License

MIT

Unpacked Size

522 kB

Total Files

17

Last publish

Collaborators

  • gnaeus