Skip to content

awaw00/react-inject-props

Repository files navigation

react-inject-props

Build Status npm version Dependency Status

Inject props to your react component.

💾 Installation

npm i react inversify react-inject-props --save

or

yarn add react inversify react-inject-props

with typescript, you should install the "reflect-metadata" package as well:

npm i reflect-metadata --save or yarn add reflect-metadata

▶ Usage

Decorators creation

you can create decorators InjectProps and ProvideProps use createPropsDecorators function with a inversify container instance as the root container:

import { Container } from 'inversify';
import { createPropsDecorators } from 'react-inject-props';

const container = new Container();
const {InjectProps, ProvideProps, containerManager} = createPropsDecorators(container);

if rootContainer not specified, createPropsDecorators will create it internally, you can take it from the function result:

import { createPropsDecorators } from 'react-inject-props';

const {InjectProps, ProvideProps, containerManager} = createPropsDecorators();
const rootContainer = containerManager.rootNode.container;

Class injection

import { injectable } from 'inversify';
import React from 'react';
import { render } from 'react-dom';

@injectable()
class Service {
  greeting () {
    console.log('hello world');
  }
}

// or simply
// @ProvideProps([
//   Service
// ])
@ProvideProps([
  {provide: Service, useClass: Service}
])
export class App extends React.Component {
  render () {
    return ...;
  }
}

interface PageProps {
  service?: Service;
}

@InjectProps({
  service: Service
})
export class Page extends React.Component<PageProps> {
  componentDidMount () {
    this.props.service!.greeting();
  }
  ...
}

render((
  <App>
    <Page/>
  </App>
), document.getElementById('root'));

in Page component, we can access Service instance by this.props.service, because we bind Service to container at App component with ProvideProps decorator and "Inject" it as this.props.service by InjectProps.

Value Injection

interface AppConfig {
  siteName: string;
}

const AppConfigToken = Symbol('appConfig');
const appConfig: AppConfig = {
  siteName: 'Github'
};

@ProvideProps([
  {provide: AppConfigToken, useValue: appConfig}
])
export class App extends React.Component {
}

interface PageProps {
  appConfig?: AppConfig
}

@InjectProps({
  appConfig: AppConfigToken
})
export class Page extends React.Component<PageProps> {
  componentDidMount () {
    console.log(this.props.appConfig!.siteName);
  }
}

Factory Injection(dynamic value)

@injectable()
class Service {
  getConfig () {
    return {
      siteName: 'Github'
    };
  }
}
const AppConfigToken = Symbol('appConfig');
function getAppConfig (service: Service) {
  return service.getConfig();
}

@ProvideProps([
  Service,
  {provide: AppConfigToken, useFactory: getAppConfig, deps: [Service]}
])
export class App extends React.Component {
}

interface PageProps {
  appConfig?: AppConfig
}

@InjectProps({
  appConfig: AppConfigToken
})
export class Page extends React.Component<PageProps> {
  componentDidMount () {
    console.log(this.props.appConfig!.siteName);
  }
}

hierarchical injection

we can use multiple ProvideProps decorators in different hierarchies to implement an hierarchical injection system.

@injectable()
class Service {
  id = Math.random();
}
@ProvideProps([
  Service
])
class App extends React.Component {}

@InjectProps({
  service: Service
})
class PageA extends React.Component {}

@InjectProps({
  service: Service
})
class CompInPageA extends React.Component {}

@ProvideProps([
  Service
])
@InjectProps({
  service: Service
})
class PageB extends React.Component {}


render((
  <App>
    <PageA>
      <CompInPageA/>
    </PageA>
    <PageB/>
  </App>
), ...);

in the above example, PageA.props.service is equals to CompInPageA.props.service and difference to PageB.props.service, case PageB is reprovide a Service.

Use existing provider if already bounded

@ProvideProps([
  {provide: Service, useClass: Service, useExisting: true}
])
class Comp extends React.Component { }

render ((
  <App>
    <Comp/>
  </App>
), ...);

if Service can be resolved in App's providers or rootContainer, the Service provider will be ignored.

Get container instance

import { Container } from 'inversify';

interface CompProps {
  container: Container;
}

@ProvideProps([
  Service
])
class Comp extends React.Component<CompProps> {
  componentDidMount () {
    const {container} = this.props;
    ...
  }
}

container of current component's hierarchy will also inject in component.props when use ProvideProps or InjectProps decorator.

Use ContainerContext directly

import { ContainerContext } from 'react-inject-props';

// or

import { createPropsDecorators } from 'react-inject-props';
const {ContainerContext} = createPropsDecorators();

// then use it in your component

class Comp extends React.Component {
  render () {
    return (
      <ContainerContext.Provider value={xxx}>
        ...
          ...
            <ContainerContext.Consumer>
              {(container: Container) => {
                ...
              }}
            </ContainerContext.Consumer>
          ...
        ...
      </ContainerContext.Provider>
    );
  }
}