An open API service indexing awesome lists of open source software.

https://github.com/robinbuschmann/react.di

Dependency injection for react based upon inversify.
https://github.com/robinbuschmann/react.di

Last synced: about 1 year ago
JSON representation

Dependency injection for react based upon inversify.

Awesome Lists containing this project

README

          

[![NPM](https://img.shields.io/npm/v/react.di.svg)](https://www.npmjs.com/package/react.di)
[![Build Status](https://travis-ci.org/RobinBuschmann/react.di.svg?branch=master)](https://travis-ci.org/RobinBuschmann/react.di)
[![codecov](https://codecov.io/gh/RobinBuschmann/react.di/branch/master/graph/badge.svg)](https://codecov.io/gh/RobinBuschmann/react.di)

# react.di
Dependency injection for react based upon [inversify](https://github.com/inversify/InversifyJS).

- [Installation](#installation)
- [Getting started](#getting-started)
- [Injection](#injection)
- [Injection from class references](#injection-from-class-references)
- [Injection via tokens](#injection-via-tokens)
- [Multi injection](#multi-injection)
- [Inject into React Components](#inject-into-react-components)
- [Inject into Services](#inject-into-services)
- [Sharing providers](#sharing-providers)
- [Importing modules](#importing-modules-in-other-modules)
- [Hierarchical shared providers](#hierarchical-shared-providers)
- [Testing](#testing)
- [API](#api)
- [Injectable](#injectable)
- [Inject](#inject)
- [Module](#module)
- [providers (Option)](#providers-option)
- [Injecting a class constructor](#injecting-a-class-constructor)
- [Injecting a value](#injecting-a-value)
- [Injection via factories](#injection-via-factories)
- [imports (Option)](#imports-option)
- [autoBindInjectable (Option)](#autobindinjectable-option)
- [TestBed (React Component)](#test-bed-react-component)
- [Provider (React Component)](#provider-react-component)

## Installation
```
npm install react.di@next reflect-metadata --save
```

Your `tsconfig.json` needs to be configured with the following flags:
```
"experimentalDecorators": true,
"emitDecoratorMetadata": true
```

**For version 1.0 see https://github.com/RobinBuschmann/react.di/tree/1.0**

## Getting started
#### 1. Define injectables (values/class)
*Service*
```typescript
import {Injectable, Inject} from 'react.di';

@Injectable
export class UserService {
constructor(@Inject private httpClient: SomeHttpClient) {}

getUser() {}
}
```
*Config*
```typescript
const CONFIG_TOKEN = 'config'; // or Symbol('config');
const config: Config = {...};
```
#### 2. Inject service/value into react component
```typescript jsx
import {Inject} from 'react.di';

class UserContainer extends Component {
@Inject userService: UserService;
@Inject(CONFIG_TOKEN) config: Config;

async componentDidMount() {
const user = await this.userService.getUser();
this.setState({user});
}

render() {
return ();
}
}
```
#### 3. Setup module
```typescript jsx
import {Module} from 'react.di';

@Module({
providers: [
{provide: UserService, useClass: UserService},
UserService, // or shorthand
{provide: CONFIG_TOKEN, useValue: config},
{provide: CONFIG_TOKEN, useFactory: context => config}, // or using factory
]
})
class App extends Component {
render() {
return (



);
}
}
```

## Injection

### Injection from class references
If you want to inject a service instance, simply mark the property with `@Inject`.
The type from which the instance will be created, will be derived from the
annotated type. Notice, that the type needs to be a class. An interface won't
work, cause it will not exist on runtime anymore (See
[Injection via tokens](#injection-via-tokens) to get it work with interfaces).
```typescript
@Inject userService: UserService;
```
A services class need to be annotated with `@Injectable` otherwise it will not
be - of course - injectable.

### Injection via tokens
Dependencies can also be identified by a token. This makes sense when injecting
a value (for example a configuration) or if you want annotate these properties
with interfaces instead of class references.
```typescript
@Inject(CONFIG_TOKEN) config: Config;
```

### Multi injection
Multi injection means that all providers for a specified identifier
will be injected.
So if the annotated type of the dependency is of type `Array`, the
injection will automatically processed as a multi-injection. In this
case the identifier needs to be passed explicitly:
```typescript
@Inject(INTERCEPTOR_TOKEN) interceptors: Interceptor[];
```

### Inject into React Components
Dependencies of react component need be injected via property injection:
```typescript jsx
import {Inject} from 'react.di';

class UserComponent extends Component {
@Inject userService: UserService;
@Inject(OtherService) otherService: OtherService;
@Inject(CONFIG_TOKEN) config: Config;
@Inject(TRANSLATION_TOKEN) translations: Translation[];

...
}
```

### Inject into Services
Dependencies of services will be injected via constructor injection:
```typescript
import {Injectable, Inject} from 'react.di';

@Injectable
export class UserService {
constructor(@Inject http: HttpClient,
@Inject(OtherService) otherService: OtherService,
@Inject(HISTORY_TOKEN) history: IHistory) {}
}
```

## Sharing providers
Providers can be shared via importing from siblings or inheriting them from
parent components.

#### Importing modules in other modules
Modules can be imported in other modules to share its providers.
```typescript jsx
@Module({providers: [CommonService]})
class CommonModule extends Component {}

@Module({
imports: [CommonModule],
providers: [UserService],
})
class UserModule extends Component {
@Inject userService: UserService;
@Inject commonService: CommonService;
}

@Module()
class Root extends Component {
render() {
return (

);
}
```

#### Hierarchical shared providers
Nesting `Module` annotated components in another `Module` annotated components
is supported.
All defined providers of the parent module will be inherited to its
child modules:
```typescript jsx
@Module({providers: [UserService]})
class UserModule extends Component {}

@Module({providers: [CommonService]})
class App extends Component {
render() {
return (
// CommonService will also be available in UserModule
);
}
```
Providers that are already defined in a parent, will be overridden
if they are also defined in its child.

## Testing
For testing purposes `react.di` provides a `TestBed` component. Nest the
components you want to test in `TestBed` and setup its dependencies in
the `providers` prop of `TestBed`:
```typescript jsx
import {mount} from 'enzyme';

mount(



);
```
or for defining a much simpler mock:
```typescript jsx
mount(



);
```

## API

### Injectable
`Injectable` decorator marks a class as available to the inversify `Container`.
```typescript
import {Injectable} from 'react.di';

@Injectable
export class UserService {}
```

### Inject
The `Inject`/`Inject(Identifier)` decorator tells the di-system what need to be injected
and in which property the injected value should be referenced.

### Module
All components that should be feedable with the defined providers,
need to be nested in a module annotated component - But don't(!) need to be
direct children.
```typescript jsx
@Module({
providers: [...],
imports: [...],
autoBindInjectable: true, // default is false
})
class App extends Component {}
```

#### `providers` (Option)
(optional) Array of all available providers.

##### Injecting a class constructor
```typescript
[{provide: UserService, useClass: UserService}]
```
*Shorthand*
```typescript
[UserService]
```
All instantiated dependencies will be a **singleton** by default. If you don't
want a dependency to be singleton set `noSingleton` to `true`:
```typescript
[{provide: UserService, useClass: UserService, noSingleton: true}]
```

##### Injecting a value
```typescript
[{provide: UserService, useValue: someUserService}]
```

##### Injection via factories
Dependencies can be injected via factories. A factory is a simple function,
that gets the context of the current scope and returns the value, that
will be injected.
```typescript
[{provide: UserService, useFactory: context => someValue}]
```

#### `imports` (Option)
(optional) Array of all modules that should be imported.
```typescript
[CommonModule, UserModule]
```

#### `autoBindInjectable` (Option)
(optional, default: `false`) When `autoBindInjectable` is set to `true`, injectable
class constructors don't need to be defined as providers anymore.
They will be available for injection by default.
So that `[{provide: UserService, useClass: UserService}]` or `[UserService]`
can be omitted:
```typescript
@Module({autoBindInjectable: true})
class App extends Component{}
```
```jsx

... // UserService will be available anyway

```

#### TestBed (React Component)
```typescript jsx

...

```

#### Provider (React Component)
The `` component is a react component, that provides low-level
support for inversifyJS containers. In other words: It takes an
inversify container as property. So if you want to use all features
of inversify, this is the component you will fall in love with:
```typescript jsx
const container = new Container();
container.bind(Ninja).to(Samurai);

...

```