/ ReactJS

Angular2React - How to make $injector globally available using ReactJS Context API.

If you've read any other pages on my blog you may know that an Application I work on uses AngularJS as the framework and ReactJS as a component library. So all of my page routes are defined in angular and then they resolve to a React Component. In order to take advantage of all of the AngularJS work we've already done, I use React2Angular to inject $injector as a prop into my parent container component. Up until this point, I then pass $injector down as props to children components. Here's an example:

Here's my main page definition. Pay attention to...

  • It defines a new module & state/route with AngularJS and maps it to a component which is a React2Angular component.
  • It imports a custom service and adds it as a requirement of my module so it so it's available from $injector.
  • It imports a ReactJS component fooContainer which is set as the source component in the react2angular function
  • It injects $injector as a prop to my component.

Now let's look at that fooContainer.js.

And finally the child component which uses the $injector:

What I'm trying to avoid is having to explicitly pass down $injector through the entire tree. This isn't a big deal when the component using $injector is a direct descendant of the main component but what if 5 levels deep a component suddenly needs access to a service and needs $injector to get it? That means 4 other components will need $injector passed as props and pass them on to their children. This is dirty because if we refactor code we have to be aware of who needs $injector below it. Can we avoid this using the Context API? Let's try!

First I'm going to create a new ReactJS Context

Next we're going use this Context in my main component file. Notice I'm no longer passing $injector in as a prop.

Now we can get at that Context in our child component file but we have to do return a context-wrapped version of the component.

With this setup we should be able to copy paste the usage of this component any where in the descendant component tree without having to worry about who's passing it the $injector prop.

I would like to avoid having to export an extra functional component to wrap my component. Doing it this way adds extra bulk to the code and I lose the ability for my editor to use PropTypes and auto-complete like here:
Screen-Shot-2019-02-07-at-4.57.50-PM

To get around this limitation I have to define that functional component to a named variable and then add the PropTypes to that component like so, where AggregatePageHeaderContainerPre is the real component definition before having context added to it.
Screen-Shot-2019-02-07-at-4.59.46-PM

There should be away to do it cleaner using Class.contextType but for the life of me I can't get it to work. You can see in the example below I should be able to just define a contextType prop just like propTypes and it should apply the context and make it available as this.context in the life-cycle methods.
Screen-Shot-2019-02-07-at-4.47.57-PM

I've tried it a few times and every time this.context is never set to the context I define. 🤷‍♂️ Here's a working example of it but when I tried to incorporate this directly into my code this.context ends up being an empty object {}. So I dunno.


Corey Snyder

Corey Snyder

Senior Front-End Engineer for Aver Inc.. I have independently developed & released multiple video-games. I play Ice Hockey, I race FPV Drones, and I love my Subaru WRX STI.

Read More