Giter VIP home page Giter VIP logo

recontainer's Introduction


recontainer


Light and simple dependency injection for TypeScript and JavaScript applications.


gzip size

Can be used in any Node.js or browser applications. Built-in integration with React.

Installation

With yarn

$ yarn add recontainer
$ yarn add recontainer-react # optional

With npm

$ npm install recontainer
$ npm install recontainer-react # optional

Please note that types are included, thus there is no need to install @types/recontainer package

Why?

In most cases, ES6 modules are efficient way to share dependencies throughout application.

There are some situations though, where you need a single point of configuration to achieve better decoupling between multiple components. One of these cases is server-side rendering, in which you might want to change implementation that depends on browser APIs to something more suitable for a Node.js application.

Moreover, using global instances imported as ES6 module can lead to memory-leaks and bugs on server-side. Take for example a global instance of event publisher, to which application subscribes on each request. Since it is not disposed after each request, any failure to unsubscribe will cause a linear leak of memory.

See server side example in JavaScript

server.jsx

import React from 'react';
import express from 'express';
import { renderToString } from 'react-dom/server';
import { createContainer } from 'recontainer';
import { createInject, ContainerProvider } from 'recontainer-react';

class Greeting extends React.Component {
  render() {
    return (
      <h1>{this.props.greeting}</h1>
    )
  }
}

const inject = createInject();
const GreetingContainer = inject('greeting')(Greeting);

const greetingFactory = container => `Hello, ${container.get('name')}!`;
const app = express();

app.use((req, res, next) => {
  const container = createContainer({ // Container gets disposed after each request
    name: () => 'John',
    greeting: greetingFactory,
  });

  const html = renderToString(
    <ContainerProvider container={container}>
      <GreetingContainer /> {/* <h1>Hello, John!</h1> */}
    </ContainerProvider>
  );

  res.send(html);
});

app.listen(3000, () => {
  console.log(`Listening on port 3000.`);
});

Why not Redux?

Redux is great at distributing state throughout application and decoupling your React components. Although you could share almost any object via store, it is generally a good practice to keep the state serializable. Many redux-related libraries (eg. next-redux-wrapper) will not work if the state couldn't be serialized.

Functions and object instances (eg. Promise) are not serializable, thus we should keep them elsewhere.

Features

  • 100% Type-safe

    When used with TypeScript, recontainer protects you from type errors and allows you to extract the full potential of your editor's code completion features

  • Light-weight

    Total module size is 914 bytes gzipped

  • Simple

Examples

TypeScript

types.ts

export interface User {
  id: string;
  name: string;
}

container.ts

import { ContainerConfig } from 'recontainer';
import { createInject, createContainerHook } from 'recontainer-react';
import { User } from './types';

export interface Dependencies {
  user: User;
  greeting: string;
  greetingProvider: (user: User) => string;
}

const user = {
  id: 'john-doe',
  name: 'John Doe',
};

export const config: ContainerConfig<Dependencies> = {
  user: () => user,
  greetingProvider: () => (user: User) => `Hello ${user.name}!`,
  greeting: container => {
    const greetingProvider = container.get('greetingProvider');
    const user = container.get('user');

    return greetingProvider(user);
  },
};

export const useContainer = createContainerHook<Dependencies>();
export const inject = createInject<Dependencies>();

Greeter.tsx

Using useContainer hook
import * as React from 'react';
import { useContainer } from './container';

const Greeter: React.FunctionComponent = () => {
  const { greeting, user } = useContainer('greeting', 'user');
  
  return (
    <div>
      <h1>{greeting}</h1>
      ID: {user.id}
    </div>
  );
}

// OR
const Greeter: React.FunctionComponent = () => {
  // Note that useContainer returns container instance when called without arguments
  const container = useContainer();
  
  return (
    <div>
      <h1>{container.get('greeting')}</h1>
      ID: {container.get('user').id}
    </div>
  );
}

export default Greeter;
Using inject
import * as React from 'react';
import { inject } from './container';

interface GreeterProps {
  greeting: string;
  user: User;
}

const Greeter: React.FunctionComponent<GreeterProps> = ({
  user,
  greeting,
}) => (
  <div>
    <h1>{greeting}</h1>
    ID: {user.id}
  </div>
);

export default inject('greeting', 'user')(Greeter);
Using withContainer
import * as React from 'react';
import { withContainer, ContainerProps } from 'recontainer-react';
import { Dependencies } from './container';

interface GreeterProps extends ContainerProps<Dependencies> {
  
}

const Greeter: React.FunctionComponent<GreeterProps> = ({
  container
}) => (
  <div>
    <h1>{container.get('greeting')}</h1>
    ID: {container.get('user').id}
  </div>
);

export default withContainer(Greeter);

App.tsx

import * as React from 'react';
import { createContainer } from 'recontainer';
import { ContainerProvider } from 'recontainer-react';
import { config } from './container';
import Greeter from './Greeter';

const container = createContainer(config);

export const App: React.FunctionComponent = () => (
  <ContainerProvider container={container}>
    <Greeter />
    {/* 
        <div>
          <h1>Hello John Doe!</h1>
          ID: john-doe
        </div>
    */}
  </ContainerProvider>
);
JavaScript

container.js

import { createInject, createContainerHook } from 'recontainer-react';

const user = {
  id: 'john-doe',
  name: 'John Doe',
};

export const config = {
  user: () => user,
  greetingProvider: () => user => `Hello ${user.name}!`,
  greeting: container => {
    const greetingProvider = container.get('greetingProvider');
    const user = container.get('user');

    return greetingProvider(user);
  },
};

export const useContainer = createContainerHook();
export const inject = createInject();

Greeter.jsx

Using useContainer hook
import React from 'react';
import { useContainer } from './container';

const Greeter = () => {
  const { greeting, user } = useContainer('greeting', 'user');

  return (
    <div>
      <h1>{greeting}</h1>
      ID: {user.id}
    </div>
  );
}

export default Greeter;
Using inject
import React from 'react';
import { inject } from './container';

const Greeter = ({
  user,
  greeting,
}) => (
  <div>
    <h1>{greeting}</h1>
    ID: {user.id}
  </div>
);

export default inject('greeting', 'user')(Greeter);
Using withContainer
import React from 'react';
import { withContainer } from 'recontainer';

const Greeter = ({
  container
}) => (
  <div>
    <h1>{container.get('greeting')}</h1>
    ID: {container.get('user').id}
  </div>
);

export default withContainer(Greeter);

App.jsx

import React from 'react';
import { createContainer, ContainerProvider } from 'recontainer';
import { config } from './container';
import Greeter from './Greeter';

const container = createContainer(config);

export const App = () => (
  <ContainerProvider container={container}>
    <Greeter />
    {/* 
        <div>
          <h1>Hello John Doe!</h1>
          ID: john-doe
        </div>
    */}
  </ContainerProvider>
);

Requirements

Docs

Container methods

get
 const container = createContainer({
   logger: () => message => console.log(message),
 });

 const log = container.get('logger');

 log('Hello World!'); // Console output: Hello World!
getAll
 const container = createContainer({
   logger: () => message => console.log(message),
   message: () => 'Foo bar',
 });

 const { logger, message } = container.getAll();

 logger(message); // Console output: Foo bar

recontainer's People

Contributors

awinogrodzki avatar

Stargazers

 avatar Richard Hess avatar Mac Młynarczyk avatar Porramate Lim avatar  avatar Adi avatar

Watchers

Witold Wasiczko avatar Richard Hess avatar

Forkers

ye-man

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.