Last active
November 11, 2017 03:24
-
-
Save skyjur/20410b8327696490f9ca89e86571570e to your computer and use it in GitHub Desktop.
Primitive dependency injection with React
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { Component, Children } from "react"; | |
| const containerKey = 'container'; | |
| interface ServiceResolver { | |
| get(serviceIdentifier: string): any; | |
| } | |
| export class Container implements ServiceResolver { | |
| private factories: {[serviceIdentifier: string]: ()=>any} = {}; | |
| constant(serviceIdentifier: string, value: any) { | |
| this.factories[serviceIdentifier] = () => value; | |
| } | |
| factory(serviceIdentifier: string, factory: () => any) { | |
| this.factories[serviceIdentifier] = factory; | |
| } | |
| singleton(serviceIdentifier: string, factory: () => any) { | |
| this.factories[serviceIdentifier] = () => { | |
| let val = factory(); | |
| this.factories[serviceIdentifier] = () => val; | |
| return val; | |
| } | |
| } | |
| get(serviceIdentifier: string): any { | |
| if(!this.factories[serviceIdentifier]) { throw new Error(`Service "${serviceIdentifier}" is not registered`); } | |
| return this.factories[serviceIdentifier].apply(this); | |
| } | |
| } | |
| export function inject(serviceIdentifier: string) { | |
| return function(target: any, propertyKey?: string, descriptor?: PropertyDescriptor) { | |
| if(!target.constructor.contextTypes) { | |
| target.constructor.contextTypes = {} | |
| } | |
| if(!target.constructor.contextTypes[containerKey]) { | |
| target.constructor.contextTypes[containerKey] = (): null => null; | |
| } | |
| Object.defineProperty(target, propertyKey, { | |
| get() { | |
| if(!this.context[containerKey] || !this.context[containerKey].get) { | |
| throw new Error(`Context should have dependency container in key "${containerKey}"`); | |
| } | |
| return this.context[containerKey].get(serviceIdentifier); | |
| } | |
| }); | |
| } | |
| } | |
| export class Provider extends Component<{container: ServiceResolver}> { | |
| static childContextTypes = { | |
| container: (): null => null | |
| } | |
| getChildContext() { | |
| return {container: this.props[containerKey]}; | |
| } | |
| render() { | |
| return Children.only(this.props.children); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import * as React from "react"; | |
| import * as TestRenderer from "react-test-renderer"; | |
| import { Container, inject, Provider } from "./di"; | |
| import { expect } from "chai"; | |
| describe('Dependency injection with React', () => { | |
| class Child extends React.Component { | |
| @inject('x') val: number; | |
| render() { return 'x is: ' + this.val; } | |
| } | |
| it.only('Should inject value', () => { | |
| let container = new Container; | |
| container.constant('x', 1); | |
| let r = TestRenderer.create(React.createElement(Provider, {container}, React.createElement(Child, {}))); | |
| expect(r.toJSON()).to.eq('x is: 1'); | |
| }); | |
| it.only('Should throw error about missing context', () => { | |
| let r = () => TestRenderer.create(React.createElement(Child, {})); | |
| expect(r).to.throw('Context should have dependency container in key "container"'); | |
| }); | |
| it.only('Should throw error about missing service', () => { | |
| let container = new Container; | |
| let r = () => TestRenderer.create(React.createElement(Provider, {container}, React.createElement(Child, {}))); | |
| expect(r).to.throw('Service "x" is not registered'); | |
| }); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment