React Contexts and Dependency Injection

In this post I want to explore the idea of using contexts in React to
implement dependency injection (DI)
in our application.

We’ll cover two things:

What contexts are in React and how they work

Why we care about DI and how we can implement them using contexts.

React and Contexts

Contexts were formalized in React 0.12, and are planned for React 1.0. They
are a mechanism for a component to pass properties down to their descendent components.
The difference between props and context is that context chains to descendents,
whereas props do not.

The current implementation couples the component to two functions: Math.round and Math.random. This
coupling makes testing impossible, and also makes it impossible to reuse if our random number provider changes.

This makes testing very easy because we now control exactly what random and round return
to our component.

describe('RandomNumber',()=>{// The context we want to return. We will rebind this in tests.letcontext;// Test container to provide dependencies.classContainer{staticchildContextTypes={random:PropTypes.func,round:PropTypes.func};getChildContext(){returncontext}render(){return<div>{this.props.children()}></div>
}}it('renders number from 1 to max',()=>{letcomponent,p;context={round:Math.round};// Binding context.context.random=()=>0.9999;// Hard-coded to return 0.9999component=TestUtils.renderIntoDocument(<Container>{()=><RandomNumbermax={10}/>}</Container>);expect(React.findDOMNode(component).textContent).to.match(/10/);context.random=()=>0.499999;component=TestUtils.renderIntoDocument(<Container>{()=><RandomNumbermax={5000}/>}</Container>);expect(React.findDOMNode(component).textContent).to.match(/2500/);});});

We have also increased reusability of our component by allowing late-binding of the random and round
functions. If we wanted to provide different rounding strategies we will be able to – say rounding
to two decimal places. We can also swap out Math.random with a better number generator if needed and
we will not need to touch RandomNumber’s implementation.

Generalization

We can extend our random number generator example to all services. In the context of Flux, we can use
DI for Stores and ActionCreators so that our components are completely decoupled from concrete types.

This is using a concept called higher-order components (HoC).
Basically, we use functions (decorators) that take in a component as input and ouputs a component. This allows
us to add additional behaviour or metadata to the original component.

Wrap-Up

In this post we saw how dependency injection can be achieved in React through the use of contexts.

We saw how dependency injection can make our code more testable and more reusable.