Skip to content

Instantly share code, notes, and snippets.

@marutypes
Last active May 18, 2017 18:52
Show Gist options
  • Save marutypes/603d3f627573f0aa3fba2303bf429ce9 to your computer and use it in GitHub Desktop.
Save marutypes/603d3f627573f0aa3fba2303bf429ce9 to your computer and use it in GitHub Desktop.

How 2 DI?

Right now our codebase has a bunch of Provider components at the top level which facilitate Higher-Order-Component based dep injection. Basically we're using #3. Is this good? Is this bad? Relay modern seems to be moving to a style more like #4, and there were some positive opinions about this it seems.

Personally, I really really don't like #4 because it is type-hostile (we need to type the props that are cloned in as optional or typescript will freak out when we don't pass them) and because it just seems the least explicit to me. That said, it seems there are some other strong opinions on this.

1. Just Imports and Singletons (Not do that)

//MyComponent.ts
import thingleton from '../../../wherever';
import otherThingleton from '../../../wherever';

function MyComponent(){
 //stuff that uses thingleton and otherThingleton
}

//consumer.ts
<MyComponent> // no injection required

//test.ts
import thingleton from '../../../wherever';
import otherThingleton from '../../../wherever';

before(() => {
  someCodeThatStubsAllTheFunctionsOnTheActualSingletons()
})

it('is cool') {
  mount(<MyComponent />)
}

2. Dependency injection HoC

//MyComponent.ts
@inject('thing', 'other-thing')
function MyComponent({thing, otherThing}: {thing: Thing, otherThing: OtherThing}){
....
}

//consumer.ts
<MyComponent /> // this has those dependencies because we used inject

//test.ts
import {mockThing, mockOtherThing} from 'test/mocks';
it('is cool') {
  mount(<MyComponent thing={mockThing} otherThing={mockOtherTHing} />)
}

3. Individual Dependency Injection HoCs

//MyComponent.ts
@withThing
@withOtherThing
function MyComponent({thing, otherThing}: {thing: Thing, otherThing: OtherThing}){
....
}

//consumer.ts
<MyComponent /> // this has those dependencies because we used inject


//test.ts
import {mockThing, mockOtherThing} from 'test/mocks';
it('is cool') {
  mount(<MyComponent thing={mockThing} otherThing={mockOtherTHing} />)
}

4. Containers that use React.cloneElement to inject props

//MyComponent.ts
function MyComponent({thing, otherThing}: {thing: Thing, otherThing: OtherThing}){
....
}

//consumer.ts
<ThingInjector><MyComponent /></ThingInjector>

//test.ts
import {mockThing, mockOtherThing} from 'test/mocks';
it('is cool') {
  mount(<MyComponent thing={mockThing} otherThing={mockOtherTHing} />)
}

5. CaF based dependency containers

//MyComponent.ts
function MyComponent({thing, otherThing}: {thing: Thing, otherThing: OtherThing}){
....
}

//consumer.ts
<ThingInjector
  children={(dependencies) => <MyComponent {...dependencies}/>}
/>

//test.ts
import {mockThing, mockOtherThing} from 'test/mocks';
it('is cool') {
  mount(<MyComponent thing={mockThing} otherThing={mockOtherTHing} />)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment